Add files to repository
This commit is contained in:
parent
9f200c9084
commit
23d704d496
542 changed files with 75775 additions and 0 deletions
14
.clang-format
Normal file
14
.clang-format
Normal file
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
Language: Cpp
|
||||
BasedOnStyle: WebKit
|
||||
SpaceAfterTemplateKeyword: false
|
||||
AlignEscapedNewlines: Left
|
||||
AlignTrailingComments: true
|
||||
BreakBeforeInheritanceComma: true
|
||||
BreakConstructorInitializers: BeforeComma
|
||||
IndentPPDirectives: AfterHash
|
||||
BreakBeforeBraces: Custom
|
||||
BraceWrapping:
|
||||
AfterFunction: true
|
||||
NamespaceIndentation: None
|
||||
QualifierAlignment: Right
|
20
.gitignore
vendored
Normal file
20
.gitignore
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
build/
|
||||
src/openlibm/*.o
|
||||
src/openlibm/*~
|
||||
src/openlibm/*.a
|
||||
src/openlibm/*.dll*
|
||||
src/openlibm/*.so*
|
||||
src/openlibm/*.dylib*
|
||||
src/openlibm/*.pc
|
||||
src/openlibm/openlibm-test
|
||||
src/openlibm/cov-html/
|
||||
src/openlibm/*.gcda
|
||||
src/openlibm/*.gcno
|
||||
src/openlibm/amd64/*.o
|
||||
src/openlibm/bsdsrc/*.o
|
||||
src/openlibm/ld80/*.o
|
||||
src/openlibm/src/*.o
|
||||
src/mujs/build/
|
||||
src/mujs/SpecialCasing.txt
|
||||
src/mujs/UnicodeData.txt
|
||||
src/mujs/one.c
|
47
.kdev4/mujs.kdev4
Normal file
47
.kdev4/mujs.kdev4
Normal file
|
@ -0,0 +1,47 @@
|
|||
[Buildset]
|
||||
BuildItems=@Variant(\x00\x00\x00\t\x00\x00\x00\x00\x01\x00\x00\x00\x0b\x00\x00\x00\x00\x01\x00\x00\x00\x08\x00m\x00u\x00j\x00s)
|
||||
|
||||
[CustomBuildSystem]
|
||||
CurrentConfiguration=BuildConfig0
|
||||
|
||||
[CustomBuildSystem][BuildConfig0]
|
||||
BuildDir=file:///home/alec/repos/mujs
|
||||
Title=
|
||||
|
||||
[CustomBuildSystem][BuildConfig0][ToolBuild]
|
||||
Arguments=/home/alec/repos/mujs
|
||||
Enabled=true
|
||||
Environment=
|
||||
Executable=file:///home/alec/repos/mujs/scripts/build-all
|
||||
Type=0
|
||||
|
||||
[CustomBuildSystem][BuildConfig0][ToolClean]
|
||||
Arguments=
|
||||
Enabled=false
|
||||
Environment=
|
||||
Executable=
|
||||
Type=3
|
||||
|
||||
[CustomBuildSystem][BuildConfig0][ToolConfigure]
|
||||
Arguments=
|
||||
Enabled=false
|
||||
Environment=
|
||||
Executable=
|
||||
Type=1
|
||||
|
||||
[CustomBuildSystem][BuildConfig0][ToolInstall]
|
||||
Arguments=
|
||||
Enabled=false
|
||||
Environment=
|
||||
Executable=
|
||||
Type=2
|
||||
|
||||
[CustomBuildSystem][BuildConfig0][ToolPrune]
|
||||
Arguments=
|
||||
Enabled=false
|
||||
Environment=
|
||||
Executable=
|
||||
Type=4
|
||||
|
||||
[Project]
|
||||
VersionControlSupport=kdevgit
|
23
MuJS.HC
Normal file
23
MuJS.HC
Normal file
|
@ -0,0 +1,23 @@
|
|||
load_elf("M:/build/bin/mujs");
|
||||
|
||||
U0 mujs(U8* filename = NULL)
|
||||
{
|
||||
I64 _argc = 1 + (filename != NULL);
|
||||
U8** _argv = CAlloc(sizeof(U8*) * _argc);
|
||||
|
||||
_argv[0] = "mujs";
|
||||
if (filename)
|
||||
_argv[1] = filename;
|
||||
|
||||
U64 reg RDI rdi = _argc;
|
||||
U64 reg RSI rsi = _argv;
|
||||
no_warn rdi, rsi;
|
||||
asm {
|
||||
MOV RAX, MUJS_MAIN
|
||||
CALL RAX
|
||||
}
|
||||
|
||||
Free(_argv);
|
||||
}
|
||||
|
||||
@patch_jmp_rel32(MUJS_EXIT, &UserTaskCont);
|
7
Run.HC
Normal file
7
Run.HC
Normal file
|
@ -0,0 +1,7 @@
|
|||
XTalkWait(Fs, "Cd(\"M:/System/\");\n");
|
||||
XTalkWait(Fs, "#include \"M:/System/MakeSystem\";\n");
|
||||
XTalkWait(Fs, "#include \"M:/MuJS\";\n");
|
||||
|
||||
XTalkWait(Fs, "Cd(\"M:/\");\n");
|
||||
XTalkWait(Fs, "mujs(\"examples/hello.js\");\n");
|
||||
|
51
System/FFI/Base.HC
Normal file
51
System/FFI/Base.HC
Normal file
|
@ -0,0 +1,51 @@
|
|||
#define PUSH_SYSV_REGS \
|
||||
asm {PUSH RCX PUSH RDX PUSH RBX PUSH RBP PUSH RSI PUSH RDI PUSH R8 PUSH R9 PUSH \
|
||||
R10 PUSH R11 PUSH R12 PUSH R13 PUSH R14 PUSH R15}
|
||||
#define POP_SYSV_REGS \
|
||||
p0 = p0; \
|
||||
p1 = p1; \
|
||||
p2 = p2; \
|
||||
p3 = p3; \
|
||||
p4 = p4; \
|
||||
p5 = p5; \
|
||||
asm {POP R15 POP R14 POP R13 POP R12 POP R11 POP R10 POP R9 POP R8 POP RDI POP \
|
||||
RSI POP RBP POP RBX POP RDX POP RCX}
|
||||
#define GET_SYSV_ARGS \
|
||||
asm {PUSH R9 PUSH R8 PUSH RCX PUSH RDX PUSH RSI PUSH RDI} \
|
||||
I64 reg RDI p0; \
|
||||
I64 reg RSI p1; \
|
||||
I64 reg RDX p2; \
|
||||
I64 reg RCX p3; \
|
||||
I64 reg R8 p4; \
|
||||
I64 reg R9 p5; \
|
||||
asm {POP RDI POP RSI POP RDX POP RCX POP R8 POP R9}
|
||||
|
||||
#define MOV_ANS_RAX asm { MOV[&ans], RAX }
|
||||
#define MOV_PARAM0_RDI asm {MOV [¶m0], RDI}
|
||||
|
||||
I64 param0;
|
||||
I64 elf_argc;
|
||||
U8** elf_argv;
|
||||
|
||||
asm {
|
||||
_ELF_CALL::
|
||||
PUSH RBP
|
||||
MOV RBP,RSP
|
||||
MOV RAX,U64 SF_ARG1[RBP]
|
||||
MOV RDI,U64 SF_ARG2[RBP]
|
||||
MOV RSI,U64 SF_ARG3[RBP]
|
||||
TEST RAX,RAX
|
||||
JZ @@05
|
||||
CALL RAX
|
||||
@@05: POP RBP
|
||||
RET1 8
|
||||
}
|
||||
|
||||
U0 _main()
|
||||
{
|
||||
MOV_PARAM0_RDI
|
||||
CallInd(_ELF_CALL, param0, elf_argc, elf_argv);
|
||||
UserTaskCont;
|
||||
}
|
||||
|
||||
//U0 _exit() { UserTaskCont; }
|
300
System/FFI/ELF64.HC
Normal file
300
System/FFI/ELF64.HC
Normal file
|
@ -0,0 +1,300 @@
|
|||
#define EI_NIDENT 16
|
||||
#define EM_X86_64 0x3E
|
||||
#define ET_EXEC 2
|
||||
#define ET_DYN 3
|
||||
|
||||
U0 @elf64_debug_print(U8 fmt, ...)
|
||||
{
|
||||
// FIXME: Remove unnecessary debug_print statements and PrintErr for errors.
|
||||
no_warn fmt, argc, argv;
|
||||
}
|
||||
|
||||
class Elf64_Ehdr {
|
||||
U8 e_ident[EI_NIDENT]; /* Magic number and other info */
|
||||
U16 e_type; /* Object file type */
|
||||
U16 e_machine; /* Architecture */
|
||||
U32 e_version; /* Object file version */
|
||||
U64 e_entry; /* Entry point virtual address */
|
||||
U64 e_phoff; /* Program header table file offset */
|
||||
U64 e_shoff; /* Section header table file offset */
|
||||
U32 e_flags; /* Processor-specific flags */
|
||||
U16 e_ehsize; /* ELF header size in bytes */
|
||||
U16 e_phentsize; /* Program header table entry size */
|
||||
U16 e_phnum; /* Program header table entry count */
|
||||
U16 e_shentsize; /* Section header table entry size */
|
||||
U16 e_shnum; /* Section header table entry count */
|
||||
U16 e_shstrndx; /* Section header string table index */
|
||||
};
|
||||
|
||||
class Elf64_Shdr {
|
||||
U32 sh_name; /* Section name (string tbl index) */
|
||||
U32 sh_type; /* Section type */
|
||||
U64 sh_flags; /* Section flags */
|
||||
U64 sh_addr; /* Section virtual addr at execution */
|
||||
U64 sh_offset; /* Section file offset */
|
||||
U64 sh_size; /* Section size in bytes */
|
||||
U32 sh_link; /* Link to another section */
|
||||
U32 sh_info; /* Additional section information */
|
||||
U64 sh_addralign; /* Section alignment */
|
||||
U64 sh_entsize; /* Entry size if section holds table */
|
||||
};
|
||||
|
||||
class Elf64_Sym {
|
||||
U32 st_name; /* Symbol name (string tbl index) */
|
||||
U8 st_info; /* Symbol type and binding */
|
||||
U8 st_other; /* Symbol visibility */
|
||||
U16 st_shndx; /* Section index */
|
||||
U64 st_value; /* Symbol value */
|
||||
U64 st_size; /* Symbol size */
|
||||
};
|
||||
|
||||
class PLT_entry {
|
||||
U8 pad[0x10];
|
||||
};
|
||||
|
||||
class RELA_entry {
|
||||
U64 r_offset;
|
||||
U64 r_info;
|
||||
I64 r_addend;
|
||||
};
|
||||
|
||||
class Elf {
|
||||
union {
|
||||
U8* u8;
|
||||
Elf64_Ehdr* ehdr;
|
||||
} I64 size;
|
||||
U8* dynstr;
|
||||
Elf64_Sym* dynsym;
|
||||
PLT_entry* plt;
|
||||
RELA_entry* rela_dyn;
|
||||
RELA_entry* rela_plt;
|
||||
Elf64_Sym* strtab;
|
||||
Elf64_Sym* symtab;
|
||||
I64 rela_dyn_size;
|
||||
I64 rela_plt_size;
|
||||
I64 strtab_size;
|
||||
I64 symtab_size;
|
||||
};
|
||||
|
||||
U0 (*_start)();
|
||||
|
||||
U0 unimplemented_symbol()
|
||||
{
|
||||
I32 s = 0xDEADF00D;
|
||||
PrintWarn("Unimplemented symbol: %s\n", s);
|
||||
Dbg;
|
||||
while (1)
|
||||
Sleep(1);
|
||||
}
|
||||
|
||||
Bool is_valid_elf(Elf* elf)
|
||||
{
|
||||
Bool res = TRUE;
|
||||
if (MemCmp(elf->u8 + 1, "ELF", 3)) {
|
||||
@elf64_debug_print("Invalid signature (not ELF).\n");
|
||||
res = FALSE;
|
||||
}
|
||||
if (elf->ehdr->e_type != ET_EXEC && elf->ehdr->e_type != ET_DYN) {
|
||||
@elf64_debug_print("Invalid object file type.\n");
|
||||
res = FALSE;
|
||||
}
|
||||
if (elf->ehdr->e_machine != EM_X86_64) {
|
||||
@elf64_debug_print("Invalid architecture.\n");
|
||||
res = FALSE;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
U0 process_elf_section_header_table(Elf* elf)
|
||||
{
|
||||
Elf64_Shdr* shdr = elf->u8 + elf->ehdr->e_shoff;
|
||||
Elf64_Shdr* shdr_shstrtab = shdr + elf->ehdr->e_shstrndx;
|
||||
U8* shstrtab = elf->u8 + shdr_shstrtab->sh_offset;
|
||||
I64 i = 0;
|
||||
while (i < elf->ehdr->e_shnum) {
|
||||
if (!StrCmp(shstrtab + shdr->sh_name, ".symtab")) {
|
||||
@elf64_debug_print("found symtab at 0x%08x, size = %d\n", shdr->sh_offset,
|
||||
shdr->sh_size);
|
||||
elf->symtab = elf->u8 + shdr->sh_offset;
|
||||
elf->symtab_size = shdr->sh_size;
|
||||
}
|
||||
if (!StrCmp(shstrtab + shdr->sh_name, ".strtab")) {
|
||||
@elf64_debug_print("found strtab at 0x%08x, size = %d\n", shdr->sh_offset,
|
||||
shdr->sh_size);
|
||||
elf->strtab = elf->u8 + shdr->sh_offset;
|
||||
elf->strtab_size = shdr->sh_size;
|
||||
}
|
||||
if (shdr->sh_addr) {
|
||||
MemCpy(shdr->sh_addr, elf->u8 + shdr->sh_offset, shdr->sh_size);
|
||||
if (!StrCmp(shstrtab + shdr->sh_name, ".dynstr"))
|
||||
elf->dynstr = shdr->sh_addr;
|
||||
if (!StrCmp(shstrtab + shdr->sh_name, ".dynsym"))
|
||||
elf->dynsym = shdr->sh_addr;
|
||||
if (!StrCmp(shstrtab + shdr->sh_name, ".plt"))
|
||||
elf->plt = shdr->sh_addr;
|
||||
if (!StrCmp(shstrtab + shdr->sh_name, ".rela.dyn")) {
|
||||
elf->rela_dyn = shdr->sh_addr;
|
||||
elf->rela_dyn_size = shdr->sh_size / shdr->sh_entsize;
|
||||
}
|
||||
if (!StrCmp(shstrtab + shdr->sh_name, ".rela.plt")) {
|
||||
elf->rela_plt = shdr->sh_addr;
|
||||
elf->rela_plt_size = shdr->sh_size / shdr->sh_entsize;
|
||||
}
|
||||
if (!StrCmp(shstrtab + shdr->sh_name, ".bss") || !StrCmp(shstrtab + shdr->sh_name, ".tbss")) {
|
||||
MemSet(shdr->sh_addr, NULL, shdr->sh_size);
|
||||
@elf64_debug_print(
|
||||
"Zeroed out section '%s' at physical address 0x%06x, size = %d bytes\n",
|
||||
shstrtab + shdr->sh_name, shdr->sh_addr, shdr->sh_size);
|
||||
} else
|
||||
@elf64_debug_print(
|
||||
"MemCpy section '%s' to physical address 0x%06x, size = %d bytes\n",
|
||||
shstrtab + shdr->sh_name, shdr->sh_addr, shdr->sh_size);
|
||||
if (!StrCmp(shstrtab + shdr->sh_name, ".bss")) {
|
||||
MemSet(shdr->sh_addr, NULL, shdr->sh_size);
|
||||
@elf64_debug_print("MemSet section '%s' at physical address 0x%06x to NULL, "
|
||||
"size = %d bytes\n",
|
||||
shstrtab + shdr->sh_name, shdr->sh_addr, shdr->sh_size);
|
||||
}
|
||||
}
|
||||
shdr++;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
U0 process_elf_rela_dyn_entries(Elf* elf)
|
||||
{
|
||||
I64 i;
|
||||
U8* entry_name;
|
||||
RELA_entry* rela_dyn = elf->rela_dyn;
|
||||
for (i = 0; i < elf->rela_dyn_size; i++) {
|
||||
entry_name = elf->dynstr + elf->dynsym[(rela_dyn->r_info >> 32)].st_name;
|
||||
@elf64_debug_print("rela_dyn->r_offset = %08x\n", rela_dyn->r_offset);
|
||||
@elf64_debug_print("entry name = '%s'\n", entry_name);
|
||||
if (!StrCmp(entry_name, "__libc_start_main")) {
|
||||
*(rela_dyn->r_offset)(U64*) = &_main;
|
||||
@elf64_debug_print("Set value for .rela.dyn entry '%s' to: &_main\n",
|
||||
entry_name);
|
||||
}
|
||||
if (!StrCmp(entry_name, "stdin")) {
|
||||
*(rela_dyn->r_offset)(U64*) = 0;
|
||||
@elf64_debug_print("Set value for .rela.dyn entry '%s' to: %d\n", entry_name, 0);
|
||||
}
|
||||
if (!StrCmp(entry_name, "stdout")) {
|
||||
*(rela_dyn->r_offset)(U64*) = 1;
|
||||
@elf64_debug_print("Set value for .rela.dyn entry '%s' to: %d\n", entry_name, 1);
|
||||
}
|
||||
if (!StrCmp(entry_name, "stderr")) {
|
||||
*(rela_dyn->r_offset)(U64*) = 2;
|
||||
@elf64_debug_print("Set value for .rela.dyn entry '%s' to: %d\n", entry_name, 2);
|
||||
}
|
||||
rela_dyn++;
|
||||
}
|
||||
}
|
||||
|
||||
CHashClass* get_symbol_hash_entry(U8* entry_name)
|
||||
{
|
||||
I64 i;
|
||||
CHashSrcSym* sym;
|
||||
CHashTable* tbl = Fs->hash_table;
|
||||
while (tbl) {
|
||||
for (i = 0; i < tbl->mask; i++) {
|
||||
sym = tbl->body[i];
|
||||
while (sym) {
|
||||
if (sym->type == HTT_CLASS)
|
||||
if (!StrCmp(sym->str, entry_name))
|
||||
return sym;
|
||||
sym = sym->next;
|
||||
}
|
||||
}
|
||||
tbl = tbl->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
U64 get_symbol_address(U8* entry_name)
|
||||
{
|
||||
CHash* h = HashFind(entry_name, Fs->hash_table, Fs->hash_table->mask);
|
||||
if (!h)
|
||||
return NULL;
|
||||
switch (h->type) {
|
||||
case HTT_GLBL_VAR:
|
||||
return h(CHashGlblVar*)->data_addr;
|
||||
break;
|
||||
case HTT_FUN:
|
||||
return h(CHashFun*)->exe_addr;
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
U0 process_elf_rela_plt_entries(Elf* elf)
|
||||
{
|
||||
I64 i;
|
||||
U32 handler;
|
||||
U32* patch;
|
||||
U8* entry_name;
|
||||
Bool symbol_exists;
|
||||
PLT_entry* plt = elf->plt;
|
||||
RELA_entry* rela_plt = elf->rela_plt;
|
||||
plt++;
|
||||
for (i = 0; i < elf->rela_plt_size; i++) {
|
||||
symbol_exists = FALSE;
|
||||
entry_name = elf->dynstr + elf->dynsym[(rela_plt->r_info >> 32)].st_name;
|
||||
handler = MAlloc(sizeof(unimplemented_symbol), adam_task->code_heap);
|
||||
MemCpy(handler, &unimplemented_symbol, sizeof(unimplemented_symbol));
|
||||
patch = handler + 0x0A;
|
||||
*patch = entry_name;
|
||||
@patch_jmp_rel32(plt, handler);
|
||||
@patch_call_rel32(handler + 0x16, &PrintErr);
|
||||
//@patch_call_rel32(handler + 0x21, &_exit);
|
||||
if (!StrCmp(entry_name, "__libc_start_main")) {
|
||||
symbol_exists = TRUE;
|
||||
@patch_jmp_rel32(plt, &_main);
|
||||
@elf64_debug_print("Set value for .rela.plt entry '%s' to &_main\n", entry_name);
|
||||
}
|
||||
if (get_symbol_address(entry_name)) {
|
||||
symbol_exists = TRUE;
|
||||
@patch_jmp_rel32(plt, get_symbol_address(entry_name));
|
||||
@elf64_debug_print("Set value for .rela.plt entry '%s' to &%s\n", entry_name,
|
||||
entry_name);
|
||||
}
|
||||
if (!symbol_exists)
|
||||
@elf64_debug_print(
|
||||
"Set value for .rela.plt entry '%s' to &unimplemented_symbol\n",
|
||||
entry_name);
|
||||
rela_plt++;
|
||||
plt++;
|
||||
}
|
||||
}
|
||||
|
||||
U0 load_elf(...)
|
||||
{
|
||||
if (argc < 1) {
|
||||
PrintErr("Not enough arguments.\n");
|
||||
return;
|
||||
}
|
||||
if (!FileFind(argv[0])) {
|
||||
PrintErr("File not found: %s\n", argv[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
Elf elf;
|
||||
elf.u8 = FileRead(argv[0], &elf.size);
|
||||
@elf64_debug_print("Load file '%s', size = %d bytes\n", argv[0], elf.size);
|
||||
|
||||
if (!is_valid_elf(&elf)) {
|
||||
PrintErr("File is not a valid ELF x86-64 executable.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
process_elf_section_header_table(&elf);
|
||||
process_elf_rela_dyn_entries(&elf);
|
||||
process_elf_rela_plt_entries(&elf);
|
||||
|
||||
_start = elf.ehdr->e_entry;
|
||||
elf_argc = argc;
|
||||
elf_argv = argv;
|
||||
}
|
373
System/FFI/LibC.HC
Normal file
373
System/FFI/LibC.HC
Normal file
|
@ -0,0 +1,373 @@
|
|||
asm {
|
||||
_SETJMP::
|
||||
MOV [RDI], RBX // Store caller saved registers
|
||||
MOV [RDI+8], RBP // ^
|
||||
MOV [RDI+16], R12 // ^
|
||||
MOV [RDI+24], R13 // ^
|
||||
MOV [RDI+32], R14 // ^
|
||||
MOV [RDI+40], R15 // ^
|
||||
LEA RDX, [RSP+8] // go one value up (as if setjmp wasn't called)
|
||||
MOV [RDI+48], RDX // Store the new rsp pointer in env[7]
|
||||
MOV RDX, [RSP] // go one value up (as if setjmp wasn't called)
|
||||
MOV [RDI+56], RDX // Store the address we will resume at in env[8]
|
||||
XOR EAX, EAX // Always return 0
|
||||
RET
|
||||
_LONGJMP::
|
||||
XOR EAX, EAX
|
||||
CMP ESI, 1
|
||||
ADC EAX, ESI
|
||||
MOV RBX, [RDI]
|
||||
MOV RBP, [RDI + 8]
|
||||
MOV R12, [RDI + 16]
|
||||
MOV R13, [RDI + 24]
|
||||
MOV R14, [RDI + 32]
|
||||
MOV R15, [RDI + 40]
|
||||
MOV RSP, [RDI + 48]
|
||||
JMP U64 [RDI + 56]
|
||||
}
|
||||
|
||||
_extern _SETJMP U64 _setjmp(U64 jmp_buf);
|
||||
_extern _LONGJMP U64 longjmp(U64 jmp_buf, U64 ret);
|
||||
|
||||
U0 free()
|
||||
{
|
||||
PUSH_SYSV_REGS
|
||||
GET_SYSV_ARGS
|
||||
Free(p0);
|
||||
POP_SYSV_REGS
|
||||
}
|
||||
|
||||
U0 putchar()
|
||||
{
|
||||
PUSH_SYSV_REGS
|
||||
GET_SYSV_ARGS
|
||||
PutChars(p0);
|
||||
POP_SYSV_REGS
|
||||
}
|
||||
|
||||
U64 @localtime(U64 p0)
|
||||
{
|
||||
// FIXME: implement this?
|
||||
no_warn p0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
U0 localtime()
|
||||
{
|
||||
PUSH_SYSV_REGS
|
||||
GET_SYSV_ARGS
|
||||
@localtime(p0);
|
||||
POP_SYSV_REGS
|
||||
}
|
||||
|
||||
I64 @strncmp(U8* s1, U8* s2, I32 n)
|
||||
{
|
||||
U64 u1, u2;
|
||||
|
||||
while (n-- > 0) {
|
||||
u1 = *s1++;
|
||||
u2 = *s2++;
|
||||
u1 = u1 & 0xff;
|
||||
u2 = u2 & 0xff;
|
||||
if (u1 != u2)
|
||||
return u1 - u2;
|
||||
if (u1 == '\0')
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
U0 strncmp()
|
||||
{
|
||||
PUSH_SYSV_REGS
|
||||
GET_SYSV_ARGS
|
||||
@strncmp(p0, p1, p2);
|
||||
POP_SYSV_REGS
|
||||
}
|
||||
|
||||
U0 strcpy()
|
||||
{
|
||||
PUSH_SYSV_REGS
|
||||
GET_SYSV_ARGS
|
||||
StrCpy(p0, p1);
|
||||
POP_SYSV_REGS
|
||||
}
|
||||
|
||||
U0 fprintf()
|
||||
{
|
||||
PUSH_SYSV_REGS
|
||||
GET_SYSV_ARGS
|
||||
Print(p1, p2, p3, p4, p5);
|
||||
POP_SYSV_REGS
|
||||
}
|
||||
|
||||
|
||||
U0 printf()
|
||||
{
|
||||
PUSH_SYSV_REGS
|
||||
GET_SYSV_ARGS
|
||||
Print(p0, p1, p2, p3, p4, p5);
|
||||
POP_SYSV_REGS
|
||||
}
|
||||
|
||||
U0 puts()
|
||||
{
|
||||
PUSH_SYSV_REGS
|
||||
GET_SYSV_ARGS
|
||||
PutS(p0);
|
||||
"\n";
|
||||
POP_SYSV_REGS
|
||||
}
|
||||
|
||||
U0 fputs()
|
||||
{
|
||||
PUSH_SYSV_REGS
|
||||
GET_SYSV_ARGS
|
||||
PutS(p0);
|
||||
POP_SYSV_REGS
|
||||
}
|
||||
|
||||
U0 fputc()
|
||||
{
|
||||
PUSH_SYSV_REGS
|
||||
GET_SYSV_ARGS
|
||||
PutChars(p0);
|
||||
POP_SYSV_REGS
|
||||
}
|
||||
|
||||
U64 @fwrite(U8* buffer, I64 size, I64 count, U64 stream)
|
||||
{
|
||||
if (!buffer || !size || !count || !stream)
|
||||
return NULL;
|
||||
I64 bytes = size * count;
|
||||
switch (stream) {
|
||||
case 1...2:
|
||||
U8* s = CAlloc(bytes + 1);
|
||||
MemCpy(s, buffer, bytes);
|
||||
"%s", s;
|
||||
Free(s);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
U0 fwrite()
|
||||
{
|
||||
PUSH_SYSV_REGS
|
||||
GET_SYSV_ARGS
|
||||
@fwrite(p0, p1, p2, p3);
|
||||
POP_SYSV_REGS
|
||||
}
|
||||
|
||||
U0 strerror()
|
||||
{
|
||||
PUSH_SYSV_REGS
|
||||
GET_SYSV_ARGS
|
||||
"strerror: %d\n", p0;
|
||||
PressAKey;
|
||||
POP_SYSV_REGS
|
||||
}
|
||||
|
||||
U0 strlen()
|
||||
{
|
||||
PUSH_SYSV_REGS
|
||||
GET_SYSV_ARGS
|
||||
StrLen(p0);
|
||||
POP_SYSV_REGS
|
||||
}
|
||||
|
||||
U8 *@strchr(U8 *str, I64 chr) {
|
||||
while (*str) {
|
||||
if (*str == chr) {
|
||||
return str;
|
||||
}
|
||||
str++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
U0 strchr()
|
||||
{
|
||||
PUSH_SYSV_REGS
|
||||
GET_SYSV_ARGS
|
||||
@strchr(p0, p1);
|
||||
POP_SYSV_REGS
|
||||
}
|
||||
|
||||
U8* @strrchr(U8 *str, I64 chr) {
|
||||
U8* str2 = str + (StrLen(str) - 1);
|
||||
while (str2 > str - 1) {
|
||||
if (*str2 == chr) {
|
||||
return str2;
|
||||
}
|
||||
str2--;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
U0 strrchr()
|
||||
{
|
||||
PUSH_SYSV_REGS
|
||||
GET_SYSV_ARGS
|
||||
@strrchr(p0, p1);
|
||||
POP_SYSV_REGS
|
||||
}
|
||||
|
||||
U0 strcmp()
|
||||
{
|
||||
PUSH_SYSV_REGS
|
||||
GET_SYSV_ARGS
|
||||
//"strcmp: '%s', '%s'\n", p0, p1;
|
||||
StrCmp(p0, p1);
|
||||
POP_SYSV_REGS
|
||||
}
|
||||
|
||||
U0 memcpy()
|
||||
{
|
||||
PUSH_SYSV_REGS
|
||||
GET_SYSV_ARGS
|
||||
MemCpy(p0, p1, p2);
|
||||
POP_SYSV_REGS
|
||||
}
|
||||
|
||||
U0 memset()
|
||||
{
|
||||
PUSH_SYSV_REGS
|
||||
GET_SYSV_ARGS
|
||||
MemSet(p0, p1, p2);
|
||||
POP_SYSV_REGS
|
||||
}
|
||||
|
||||
U0 malloc()
|
||||
{
|
||||
PUSH_SYSV_REGS
|
||||
GET_SYSV_ARGS
|
||||
MAlloc(p0);
|
||||
POP_SYSV_REGS
|
||||
}
|
||||
|
||||
U8* @realloc(U8* ptr, I64 size)
|
||||
{
|
||||
U8* new;
|
||||
if (!ptr) {
|
||||
new = MAlloc(size);
|
||||
} else {
|
||||
new = MAlloc(size);
|
||||
MemCpy(new, ptr, size);
|
||||
Free(ptr);
|
||||
}
|
||||
return new;
|
||||
}
|
||||
|
||||
U0 realloc()
|
||||
{
|
||||
PUSH_SYSV_REGS
|
||||
GET_SYSV_ARGS
|
||||
@realloc(p0, p1);
|
||||
POP_SYSV_REGS
|
||||
}
|
||||
|
||||
#define MY_TIME_OFFSET 9488
|
||||
|
||||
public
|
||||
I64 CDate2Unix(CDate dt)
|
||||
{ // TempleOS datetime to Unix timestamp.
|
||||
return ToI64((dt - Str2Date("1/1/1970")) / CDATE_FREQ) - MY_TIME_OFFSET;
|
||||
}
|
||||
|
||||
I64 @time(I64* ptr)
|
||||
{
|
||||
no_warn ptr;
|
||||
return CDate2Unix(Now);
|
||||
}
|
||||
|
||||
U0 time()
|
||||
{
|
||||
PUSH_SYSV_REGS
|
||||
GET_SYSV_ARGS
|
||||
@time(p0);
|
||||
POP_SYSV_REGS
|
||||
}
|
||||
|
||||
U64 @gettimeofday(U64* tv)
|
||||
{
|
||||
if (!tv)
|
||||
return 0;
|
||||
U64 seconds = CDate2Unix(Now);
|
||||
tv[0] = seconds;
|
||||
tv[1] = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
U0 gettimeofday()
|
||||
{
|
||||
PUSH_SYSV_REGS
|
||||
GET_SYSV_ARGS
|
||||
@gettimeofday(p0);
|
||||
POP_SYSV_REGS
|
||||
}
|
||||
|
||||
U0 @fgets(U8* p0, I64 p1)
|
||||
{
|
||||
GetS(p0, p1);
|
||||
while (*p0) {
|
||||
if (*p0 == 0x1f)
|
||||
*p0 = 0x20;
|
||||
++p0;
|
||||
}
|
||||
Print("");
|
||||
}
|
||||
|
||||
U0 fgets()
|
||||
{
|
||||
PUSH_SYSV_REGS
|
||||
GET_SYSV_ARGS
|
||||
@fgets(p0, p1);
|
||||
POP_SYSV_REGS
|
||||
}
|
||||
|
||||
U64 @ferror()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
U0 ferror()
|
||||
{
|
||||
PUSH_SYSV_REGS
|
||||
GET_SYSV_ARGS
|
||||
@ferror;
|
||||
POP_SYSV_REGS
|
||||
}
|
||||
|
||||
U64 @strcat(U8* dst, U8* src)
|
||||
{
|
||||
StrCpy(dst + StrLen(dst), src);
|
||||
return 0;
|
||||
}
|
||||
|
||||
U0 strcat()
|
||||
{
|
||||
PUSH_SYSV_REGS
|
||||
GET_SYSV_ARGS
|
||||
@strcat(p0, p1);
|
||||
POP_SYSV_REGS
|
||||
}
|
||||
|
||||
U0 strstr()
|
||||
{
|
||||
PUSH_SYSV_REGS
|
||||
GET_SYSV_ARGS
|
||||
StrFind(p1, p0);
|
||||
POP_SYSV_REGS
|
||||
|
||||
}
|
||||
|
||||
U0 sprintf()
|
||||
{
|
||||
PUSH_SYSV_REGS
|
||||
GET_SYSV_ARGS
|
||||
StrPrint(p0, p1, p2, p3, p4, p5);
|
||||
POP_SYSV_REGS
|
||||
}
|
60
System/LibTemple/OS.HC
Normal file
60
System/LibTemple/OS.HC
Normal file
|
@ -0,0 +1,60 @@
|
|||
U0 os_call_ext_str()
|
||||
{
|
||||
PUSH_SYSV_REGS
|
||||
GET_SYSV_ARGS
|
||||
CallExtStr(p0);
|
||||
POP_SYSV_REGS
|
||||
}
|
||||
|
||||
U0 os_call_ext_str_1()
|
||||
{
|
||||
PUSH_SYSV_REGS
|
||||
GET_SYSV_ARGS
|
||||
CallExtStr(p0, p1);
|
||||
POP_SYSV_REGS
|
||||
}
|
||||
|
||||
U0 os_call_ext_str_2()
|
||||
{
|
||||
PUSH_SYSV_REGS
|
||||
GET_SYSV_ARGS
|
||||
CallExtStr(p0, p1, p2);
|
||||
POP_SYSV_REGS
|
||||
}
|
||||
|
||||
U0 os_call_ext_str_3()
|
||||
{
|
||||
PUSH_SYSV_REGS
|
||||
GET_SYSV_ARGS
|
||||
CallExtStr(p0, p1, p2, p3);
|
||||
POP_SYSV_REGS
|
||||
}
|
||||
|
||||
U0 os_call_ext_str_4()
|
||||
{
|
||||
PUSH_SYSV_REGS
|
||||
GET_SYSV_ARGS
|
||||
CallExtStr(p0, p1, p2, p3, p4);
|
||||
POP_SYSV_REGS
|
||||
}
|
||||
|
||||
U0 os_call_ext_str_5()
|
||||
{
|
||||
PUSH_SYSV_REGS
|
||||
GET_SYSV_ARGS
|
||||
CallExtStr(p0, p1, p2, p3, p4, p5);
|
||||
POP_SYSV_REGS
|
||||
}
|
||||
|
||||
U64 @os_jiffies()
|
||||
{
|
||||
return cnts.jiffies;
|
||||
}
|
||||
|
||||
U0 os_jiffies()
|
||||
{
|
||||
PUSH_SYSV_REGS
|
||||
GET_SYSV_ARGS
|
||||
@os_jiffies();
|
||||
POP_SYSV_REGS
|
||||
}
|
44
System/MakeSystem.HC
Normal file
44
System/MakeSystem.HC
Normal file
|
@ -0,0 +1,44 @@
|
|||
/* clang-format off */
|
||||
|
||||
WinMax;
|
||||
AutoComplete(0);
|
||||
|
||||
U0 @patch_call_rel32(U32 from, U32 to)
|
||||
{
|
||||
*(from(U8*)) = 0xE8;
|
||||
*((from + 1)(I32*)) = to - from - 5;
|
||||
}
|
||||
|
||||
U0 @patch_jmp_rel32(U32 from, U32 to)
|
||||
{
|
||||
*(from(U8*)) = 0xE9;
|
||||
*((from + 1)(I32*)) = to - from - 5;
|
||||
}
|
||||
|
||||
U0 @sse_enable()
|
||||
{
|
||||
/* clang-format off */
|
||||
asm
|
||||
{
|
||||
MOV_EAX_CR0
|
||||
AND AX, 0xFFFB // clear coprocessor emulation CR0.EM
|
||||
OR AX, 0x2 // set coprocessor monitoring CR0.MP
|
||||
MOV_CR0_EAX
|
||||
MOV_EAX_CR4
|
||||
OR AX, 3 << 9 // set CR4.OSFXSR and CR4.OSXMMEXCPT at the same time
|
||||
MOV_CR4_EAX
|
||||
}
|
||||
/* clang-format on */
|
||||
}
|
||||
|
||||
@sse_enable;
|
||||
|
||||
// FFI support files
|
||||
#include "FFI/Base";
|
||||
#include "FFI/LibC";
|
||||
#include "FFI/ELF64";
|
||||
|
||||
// LibTemple support files
|
||||
#include "LibTemple/OS";
|
||||
|
||||
/* clang-format on */
|
3
examples/hello.js
Normal file
3
examples/hello.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
print("Hello, TempleOS, from MuJS!")
|
||||
print("Current date and time is: " + JSON.stringify(new Date()))
|
||||
print("Type mujs; with no arguments for a REPL, or mujs(\"path/to/file.js\"); to run a program.")
|
60
examples/mandelbrot.js
Normal file
60
examples/mandelbrot.js
Normal file
|
@ -0,0 +1,60 @@
|
|||
/**
|
||||
* autor: foqc
|
||||
* github: foqc
|
||||
*/
|
||||
// noprotect
|
||||
|
||||
function mandelbrot(c) {
|
||||
var MAX_ITERATION = 80
|
||||
var z = { x: 0, y: 0 }, n = 0, p, d
|
||||
do {
|
||||
p = {
|
||||
x: Math.pow(z.x, 2) - Math.pow(z.y, 2),
|
||||
y: 2 * z.x * z.y
|
||||
}
|
||||
z = {
|
||||
x: p.x + c.x,
|
||||
y: p.y + c.y
|
||||
}
|
||||
d = Math.sqrt(Math.pow(z.x, 2) + Math.pow(z.y, 2))
|
||||
n += 1
|
||||
} while (d <= 2 && n < MAX_ITERATION)
|
||||
isMandelbrotSet = d <= 2
|
||||
return n
|
||||
}
|
||||
|
||||
function draw() {
|
||||
var ctx = DC.alias()
|
||||
var isMandelbrotSet = false
|
||||
var colors = []
|
||||
for (var i = 0; i < 16; i++)
|
||||
colors[i] = i === 0 ? 0 : Math.random() * 16
|
||||
|
||||
var WIDTH = 320
|
||||
var HEIGHT = 240
|
||||
|
||||
var REAL_SET = { start: -2, end: 1 }
|
||||
var IMAGINARY_SET = { start: -1, end: 1 }
|
||||
|
||||
var ox = 640 - WIDTH - 8
|
||||
var oy = 480 - HEIGHT - 8
|
||||
for (var i = 0; i < WIDTH; i++) {
|
||||
for (var j = 0; j < HEIGHT; j++) {
|
||||
complex = {
|
||||
x: REAL_SET.start + (i / WIDTH) * (REAL_SET.end - REAL_SET.start),
|
||||
y: IMAGINARY_SET.start + (j / HEIGHT) * (IMAGINARY_SET.end - IMAGINARY_SET.start)
|
||||
}
|
||||
|
||||
m = mandelbrot(complex)
|
||||
|
||||
DC.set_color(ctx, colors[isMandelbrotSet ? 0 : (m % colors.length - 1) + 1])
|
||||
Gr.plot(ctx, ox + i, oy + j)
|
||||
OS.sleep(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
draw()
|
||||
|
||||
print("Hello, TempleOS, from MuJS!")
|
||||
alert("This is an alert!")
|
4
mujs.kdev4
Normal file
4
mujs.kdev4
Normal file
|
@ -0,0 +1,4 @@
|
|||
[Project]
|
||||
CreatedFrom=
|
||||
Manager=KDevCustomBuildSystem
|
||||
Name=mujs
|
152
scripts/build-all
Executable file
152
scripts/build-all
Executable file
|
@ -0,0 +1,152 @@
|
|||
#!/usr/bin/python3 -u
|
||||
from pathlib import Path
|
||||
import glob
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
raise ValueError('wrong number of arguments')
|
||||
|
||||
project_path = sys.argv[1] + '/'
|
||||
project_name = project_path.rsplit('/')[-2]
|
||||
|
||||
isoc_file = project_path + 'build/isoc/MuJS.ISO.C'
|
||||
redsea_path = project_path + 'build/redsea'
|
||||
|
||||
home_path = str(Path.home()) + '/'
|
||||
|
||||
qemu_slipstream_iso_file = project_path + 'build/isoc/bootable.iso'
|
||||
|
||||
qemu_bin_path = "qemu-system-x86_64"
|
||||
qemu_display = "-display sdl,grab-mod=rctrl"
|
||||
|
||||
templeos_iso_file = home_path + 'iso/TempleOS.ISO'
|
||||
|
||||
qemu_run_cmd = qemu_bin_path + ' ' + qemu_display + ' -enable-kvm -m 1024 -cdrom ' + qemu_slipstream_iso_file + ' -audiodev pa,id=snd0 -machine pcspk-audiodev=snd0 -debugcon stdio -boot d'
|
||||
|
||||
def clang_format_src_files():
|
||||
print("build-all: clang-format-src-files")
|
||||
exclude_paths = ["mujs", "openlibm", ".iso.c"]
|
||||
format_file_extensions = [".c", ".cpp", ".h", ".hc"]
|
||||
for src_file in glob.glob(project_path + "**", recursive=True):
|
||||
exclude_file = False
|
||||
for exclude_path in exclude_paths:
|
||||
if src_file.lower().find(exclude_path) > 0:
|
||||
exclude_file = True
|
||||
if exclude_file:
|
||||
continue
|
||||
for format_file_extension in format_file_extensions:
|
||||
if src_file.lower().endswith(format_file_extension):
|
||||
print(src_file)
|
||||
res = os.system('clang-format -i --style=file:' + project_path + '.clang-format ' + src_file)
|
||||
if res:
|
||||
raise ValueError("build-all: step 'clang-format-src-files' failed, error code " + str(res))
|
||||
|
||||
def refresh_build_path():
|
||||
print("build-all: refresh-build-path")
|
||||
res = os.system('rm -rf ' + project_path + 'build && mkdir -p ' + project_path + 'build/bin && mkdir -p ' + project_path + 'build/isoc && mkdir -p ' + project_path + 'build/lib && mkdir -p ' + project_path + 'build/redsea')
|
||||
if res:
|
||||
raise ValueError("build-all: step 'refresh-build-path' failed, error code " + str(res))
|
||||
|
||||
def build_image():
|
||||
print("build-all: build-image")
|
||||
build_specific_options = '-Wl,--section-start=.text=0x1004000 -Wl,--section-start=.plt=0x1002020 -no-pie'
|
||||
res = os.system('cd ' + project_path + '&& cd src/image && gcc -o ../../build/bin/image ' + build_specific_options + ' -O0 -mno-mmx -mno-red-zone image.c')
|
||||
if res:
|
||||
raise ValueError("build-all: step 'build-image' failed, error code " + str(res))
|
||||
|
||||
def build_libtemple():
|
||||
print("build-all: build-libtemple")
|
||||
res = os.system('cd ' + project_path + 'src/libtemple && gcc -c -o ../../build/libtemple.o libtemple.c && gcc -shared -o ../../build/lib/libtemple.so ../../build/libtemple.o && rm ' + project_path + 'build/libtemple.o')
|
||||
if res:
|
||||
raise ValueError("build-all: step 'build-libtemple' failed, error code " + str(res))
|
||||
|
||||
def build_openlibm():
|
||||
print("build-all: build-openlibm")
|
||||
res = os.system('cd ' + project_path + '&& cd src/openlibm && make clean && make ARCH=amd64')
|
||||
if res:
|
||||
raise ValueError("build-all: step 'build-image' failed, error code " + str(res))
|
||||
|
||||
def build_mujs():
|
||||
print("build-all: build-mujs")
|
||||
#build_specific_options = '-Wl,--section-start=.text=0x1004000 -Wl,--section-start=.plt=0x1002020 -no-pie'
|
||||
res = os.system('cd ' + project_path + '&& cd src/mujs && rm -f one.c && make && cp build/debug/mujs ../../build/bin/mujs')
|
||||
if res:
|
||||
raise ValueError("build-all: step 'build-mujs' failed, error code " + str(res))
|
||||
|
||||
def address_string_for_symbol(file, symbol):
|
||||
p = subprocess.Popen('readelf -s --wide "' + file + '" | grep \'' + symbol + '$\' | awk \'{sub("000000000", "0x", $2); print $2}\'', shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||
return str(p.communicate()[0][:-1].decode(encoding='utf-8'))
|
||||
|
||||
def hc_fixup(macro, symbol, bin_path, hc_path):
|
||||
os.system('echo -e "#define ' + macro + ' ' + address_string_for_symbol(bin_path, symbol) + '\n" | cat - ' + hc_path + ' | sponge ' + hc_path)
|
||||
return
|
||||
|
||||
def exit_address_for_bin(file):
|
||||
p = subprocess.Popen('objdump -d "' + file + '" | grep exit@plt\\>: | awk \'{sub("000000000", "0x", $1); print $1}\'', shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||
return str(p.communicate()[0][:-1].decode(encoding='utf-8'))
|
||||
|
||||
def exit_fixup(bin_path, hc_path):
|
||||
os.system('echo -e "#define MUJS_EXIT ' + exit_address_for_bin(bin_path) + '\n" | cat - ' + hc_path + ' | sponge ' + hc_path)
|
||||
|
||||
def generate_iso_c_file():
|
||||
print("build-all: generate-iso-c-file")
|
||||
step_error_message = "build-all: step 'generate-iso-c-file' failed, error code "
|
||||
|
||||
res = os.system('isoc-mount --rw ' + isoc_file + ' ' + redsea_path)
|
||||
if res:
|
||||
raise ValueError(step_error_message + str(res))
|
||||
time.sleep(0.25)
|
||||
|
||||
copy_files_cmd_line = 'rsync -av --inplace --progress ' + project_path + ' ' + redsea_path
|
||||
copy_files_cmd_line += ' --exclude .clang-format'
|
||||
copy_files_cmd_line += ' --exclude .git'
|
||||
copy_files_cmd_line += ' --exclude .gitignore'
|
||||
copy_files_cmd_line += ' --exclude build/isoc'
|
||||
copy_files_cmd_line += ' --exclude build/lib'
|
||||
copy_files_cmd_line += ' --exclude build/redsea'
|
||||
copy_files_cmd_line += ' --exclude scripts'
|
||||
copy_files_cmd_line += ' --exclude src'
|
||||
res = os.system(copy_files_cmd_line)
|
||||
if res:
|
||||
raise ValueError(step_error_message + str(res))
|
||||
|
||||
# Fixup addresses for MuJS
|
||||
mujs_bin_path = redsea_path + '/build/bin/mujs'
|
||||
mujs_hc_path = redsea_path + '/MuJS.HC'
|
||||
|
||||
hc_fixup('MUJS_MAIN', 'main', mujs_bin_path, mujs_hc_path)
|
||||
exit_fixup(mujs_bin_path, mujs_hc_path)
|
||||
|
||||
time.sleep(0.25)
|
||||
|
||||
res = os.system('sync && fusermount -u ' + redsea_path)
|
||||
if res:
|
||||
raise ValueError(step_error_message + str(res))
|
||||
time.sleep(0.25)
|
||||
|
||||
def generate_slipstream_iso_file():
|
||||
print("build-all: generate-slipstream-iso-file")
|
||||
res = os.system('templeos-slipstream ' + templeos_iso_file + ' ' + isoc_file + ' ' + qemu_slipstream_iso_file)
|
||||
if res:
|
||||
raise ValueError("build-all: step 'generate-slipstream-iso-file' failed, error code " + str(res))
|
||||
|
||||
def run():
|
||||
print("build-all: run")
|
||||
res = os.system(qemu_run_cmd)
|
||||
if res:
|
||||
raise ValueError("build-all: step 'run' failed, error code " + str(res))
|
||||
|
||||
def build_all():
|
||||
clang_format_src_files()
|
||||
refresh_build_path()
|
||||
build_libtemple()
|
||||
build_openlibm()
|
||||
build_mujs()
|
||||
generate_iso_c_file()
|
||||
generate_slipstream_iso_file()
|
||||
run()
|
||||
|
||||
build_all()
|
13
src/libtemple/libtemple.c
Normal file
13
src/libtemple/libtemple.c
Normal file
|
@ -0,0 +1,13 @@
|
|||
unsigned long os_call_ext_str(char const* func) { return 0; }
|
||||
|
||||
unsigned long os_call_ext_str_1(char const* func, unsigned long arg1) { return 0; }
|
||||
|
||||
unsigned long os_call_ext_str_2(char const* func, unsigned long arg1, unsigned long arg2) { return 0; }
|
||||
|
||||
unsigned long os_call_ext_str_3(char const* func, unsigned long arg1, unsigned long arg2, unsigned long arg3) { return 0; }
|
||||
|
||||
unsigned long os_call_ext_str_4(char const* func, unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4) { return 0; }
|
||||
|
||||
unsigned long os_call_ext_str_5(char const* func, unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) { return 0; }
|
||||
|
||||
unsigned long os_jiffies() { return 0; }
|
7
src/libtemple/os.h
Normal file
7
src/libtemple/os.h
Normal file
|
@ -0,0 +1,7 @@
|
|||
uint64_t os_call_ext_str(char const* func);
|
||||
uint64_t os_call_ext_str_1(char const* func, uint64_t arg1);
|
||||
uint64_t os_call_ext_str_2(char const* func, uint64_t arg1, uint64_t arg2);
|
||||
uint64_t os_call_ext_str_3(char const* func, uint64_t arg1, uint64_t arg2, uint64_t arg3);
|
||||
uint64_t os_call_ext_str_4(char const* func, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4);
|
||||
uint64_t os_call_ext_str_5(char const* func, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5);
|
||||
unsigned long os_jiffies();
|
7
src/mujs/.gitattributes
vendored
Normal file
7
src/mujs/.gitattributes
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
# Define macro for whitespace settings:
|
||||
[attr]tabs whitespace=trailing-space,space-before-tab,indent-with-non-tab
|
||||
|
||||
* text=auto
|
||||
Makefile tabs
|
||||
*.[ch] tabs
|
||||
*.js tabs
|
1
src/mujs/AUTHORS
Normal file
1
src/mujs/AUTHORS
Normal file
|
@ -0,0 +1 @@
|
|||
Tor Andersson <tor.andersson@artifex.com>
|
16
src/mujs/COPYING
Normal file
16
src/mujs/COPYING
Normal file
|
@ -0,0 +1,16 @@
|
|||
ISC License
|
||||
|
||||
Copyright (c) 2013-2020 Artifex Software, Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
|
168
src/mujs/Makefile
Normal file
168
src/mujs/Makefile
Normal file
|
@ -0,0 +1,168 @@
|
|||
# Makefile for building MuJS libraries, shell, and pretty-printer.
|
||||
#
|
||||
# Useful targets are: release, install, uninstall.
|
||||
|
||||
default: build/debug/mujs build/debug/mujs-pp
|
||||
|
||||
CFLAGS = -O0 -mno-mmx -mno-red-zone -std=c99 -pedantic -Wall -Wextra -Wno-unused-parameter
|
||||
|
||||
OPTIM =
|
||||
|
||||
prefix = /usr/local
|
||||
bindir = $(prefix)/bin
|
||||
incdir = $(prefix)/include
|
||||
libdir = $(prefix)/lib
|
||||
|
||||
ifeq ($(wildcard .git),.git)
|
||||
VERSION = $(shell git describe --tags --always)
|
||||
else
|
||||
VERSION = $(patsubst mujs-%,%,$(notdir $(CURDIR)))
|
||||
endif
|
||||
|
||||
ifeq ($(shell uname),Darwin)
|
||||
SO = dylib
|
||||
else
|
||||
SO = so
|
||||
endif
|
||||
|
||||
ifeq ($(shell uname),FreeBSD)
|
||||
CFLAGS += -I/usr/local/include -L/usr/local/lib
|
||||
endif
|
||||
|
||||
HDRS = mujs.h jsi.h regexp.h utf.h astnames.h opnames.h utfdata.h
|
||||
|
||||
SRCS = \
|
||||
jsarray.c \
|
||||
jsboolean.c \
|
||||
jsbuiltin.c \
|
||||
jscompile.c \
|
||||
jsdate.c \
|
||||
jsdtoa.c \
|
||||
jserror.c \
|
||||
jsfunction.c \
|
||||
jsgc.c \
|
||||
jsintern.c \
|
||||
jslex.c \
|
||||
jslibtemple.c \
|
||||
jsmath.c \
|
||||
jsnumber.c \
|
||||
jsobject.c \
|
||||
json.c \
|
||||
jsparse.c \
|
||||
jsproperty.c \
|
||||
jsregexp.c \
|
||||
jsrepr.c \
|
||||
jsrun.c \
|
||||
jsstate.c \
|
||||
jsstring.c \
|
||||
jsvalue.c \
|
||||
regexp.c \
|
||||
utf.c
|
||||
|
||||
one.c:
|
||||
for F in $(SRCS); do echo "#include \"$$F\""; done > $@
|
||||
|
||||
astnames.h: jsi.h
|
||||
grep -E '\<(AST|EXP|STM)_' jsi.h | sed 's/^[^A-Z]*\(AST_\)*/"/;s/,.*/",/' | tr A-Z a-z > $@
|
||||
|
||||
opnames.h: jsi.h
|
||||
grep -E '\<OP_' jsi.h | sed 's/^[^A-Z]*OP_/"/;s/,.*/",/' | tr A-Z a-z > $@
|
||||
|
||||
UnicodeData.txt:
|
||||
curl -s -o $@ https://www.unicode.org/Public/16.0.0/ucd/UnicodeData.txt
|
||||
SpecialCasing.txt:
|
||||
curl -s -o $@ https://www.unicode.org/Public/16.0.0/ucd/SpecialCasing.txt
|
||||
|
||||
utfdata.h: genucd.py UnicodeData.txt SpecialCasing.txt
|
||||
python3 genucd.py UnicodeData.txt SpecialCasing.txt >$@
|
||||
|
||||
build/sanitize/mujs: main.c one.c $(SRCS) $(HDRS)
|
||||
@mkdir -p $(@D)
|
||||
$(CC) $(CFLAGS) -g -fsanitize=address -fno-omit-frame-pointer -o $@ main.c one.c ../openlibm/libopenlibm.a $(READLINE_CFLAGS) $(READLINE_LIBS)
|
||||
|
||||
build/debug/libmujs.$(SO): one.c $(SRCS) $(HDRS)
|
||||
@mkdir -p $(@D)
|
||||
$(CC) $(CFLAGS) -g -fPIC -shared -o $@ one.c ../openlibm/libopenlibm.a ../../build/lib/libtemple.so
|
||||
build/debug/libmujs.o: one.c $(SRCS) $(HDRS)
|
||||
@mkdir -p $(@D)
|
||||
$(CC) $(CFLAGS) -g -c -o $@ one.c ../../build/lib/libtemple.so
|
||||
build/debug/libmujs.a: build/debug/libmujs.o
|
||||
$(AR) cr $@ $^
|
||||
build/debug/mujs: main.c build/debug/libmujs.o
|
||||
$(CC) $(CFLAGS) -g -o $@ $^ ../openlibm/libopenlibm.a ../../build/lib/libtemple.so $(READLINE_CFLAGS) $(READLINE_LIBS)
|
||||
build/debug/mujs-pp: pp.c build/debug/libmujs.o
|
||||
$(CC) $(CFLAGS) -g -o $@ $^ ../openlibm/libopenlibm.a ../../build/lib/libtemple.so
|
||||
|
||||
build/release/libmujs.$(SO): one.c $(SRCS) $(HDRS)
|
||||
@mkdir -p $(@D)
|
||||
$(CC) $(CFLAGS) $(OPTIM) -fPIC -shared -o $@ one.c ../openlibm/libopenlibm.a ../../build/lib/libtemple.so
|
||||
build/release/libmujs.o: one.c $(SRCS) $(HDRS)
|
||||
@mkdir -p $(@D)
|
||||
$(CC) $(CFLAGS) $(OPTIM) -c -o $@ one.c
|
||||
build/release/libmujs.a: build/release/libmujs.o
|
||||
$(AR) cr $@ $^
|
||||
build/release/mujs: main.c build/release/libmujs.o
|
||||
$(CC) $(CFLAGS) $(OPTIM) -o $@ $^ ../openlibm/libopenlibm.a $(READLINE_CFLAGS) $(READLINE_LIBS)
|
||||
build/release/mujs-pp: pp.c build/release/libmujs.o
|
||||
$(CC) $(CFLAGS) $(OPTIM) -o $@ $^ ../openlibm/libopenlibm.a
|
||||
|
||||
build/release/mujs.pc:
|
||||
@mkdir -p $(@D)
|
||||
echo > $@ Name: mujs
|
||||
echo >> $@ Description: MuJS embeddable Javascript interpreter
|
||||
echo >> $@ Version: $(VERSION)
|
||||
echo >> $@ Cflags: -I$(incdir)
|
||||
echo >> $@ Libs: -L$(libdir) -lmujs
|
||||
echo >> $@ Libs.private: ../openlibm/libopenlibm.a
|
||||
|
||||
install-common: build/release/mujs build/release/mujs-pp build/release/mujs.pc
|
||||
install -d $(DESTDIR)$(incdir)
|
||||
install -d $(DESTDIR)$(libdir)
|
||||
install -d $(DESTDIR)$(libdir)/pkgconfig
|
||||
install -d $(DESTDIR)$(bindir)
|
||||
install -m 644 mujs.h $(DESTDIR)$(incdir)
|
||||
install -m 644 build/release/mujs.pc $(DESTDIR)$(libdir)/pkgconfig
|
||||
install -m 755 build/release/mujs $(DESTDIR)$(bindir)
|
||||
install -m 755 build/release/mujs-pp $(DESTDIR)$(bindir)
|
||||
|
||||
install-static: install-common build/release/libmujs.a
|
||||
install -m 644 build/release/libmujs.a $(DESTDIR)$(libdir)
|
||||
|
||||
install-shared: install-common build/release/libmujs.$(SO)
|
||||
install -m 755 build/release/libmujs.$(SO) $(DESTDIR)$(libdir)
|
||||
|
||||
install: install-static
|
||||
|
||||
uninstall:
|
||||
rm -f $(DESTDIR)$(bindir)/mujs
|
||||
rm -f $(DESTDIR)$(bindir)/mujs-pp
|
||||
rm -f $(DESTDIR)$(incdir)/mujs.h
|
||||
rm -f $(DESTDIR)$(libdir)/pkgconfig/mujs.pc
|
||||
rm -f $(DESTDIR)$(libdir)/libmujs.a
|
||||
rm -f $(DESTDIR)$(libdir)/libmujs.$(SO)
|
||||
|
||||
tarball:
|
||||
git archive --format=zip --prefix=mujs-$(VERSION)/ HEAD > mujs-$(VERSION).zip
|
||||
git archive --format=tar --prefix=mujs-$(VERSION)/ HEAD | gzip > mujs-$(VERSION).tar.gz
|
||||
|
||||
tags: $(SRCS) $(HDRS) main.c pp.c
|
||||
ctags $^
|
||||
|
||||
clean:
|
||||
rm -rf build
|
||||
|
||||
nuke: clean
|
||||
rm -f one.c astnames.h opnames.h
|
||||
|
||||
sanitize: build/sanitize/mujs
|
||||
|
||||
debug: build/debug/libmujs.a
|
||||
debug: build/debug/libmujs.$(SO)
|
||||
debug: build/debug/mujs
|
||||
debug: build/debug/mujs-pp
|
||||
|
||||
release: build/release/mujs.pc
|
||||
release: build/release/libmujs.a
|
||||
release: build/release/libmujs.$(SO)
|
||||
release: build/release/mujs
|
||||
release: build/release/mujs-pp
|
50
src/mujs/README
Normal file
50
src/mujs/README
Normal file
|
@ -0,0 +1,50 @@
|
|||
MuJS: an embeddable Javascript interpreter in C.
|
||||
|
||||
ABOUT
|
||||
|
||||
MuJS is a lightweight Javascript interpreter designed for embedding in
|
||||
other software to extend them with scripting capabilities.
|
||||
|
||||
LICENSE
|
||||
|
||||
MuJS is Copyright 2013-2017 Artifex Software, Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
The software is provided "as is" and the author disclaims all warranties with
|
||||
regard to this software including all implied warranties of merchantability
|
||||
and fitness. In no event shall the author be liable for any special, direct,
|
||||
indirect, or consequential damages or any damages whatsoever resulting from
|
||||
loss of use, data or profits, whether in an action of contract, negligence
|
||||
or other tortious action, arising out of or in connection with the use or
|
||||
performance of this software.
|
||||
|
||||
COMPILING
|
||||
|
||||
If you are building from source you can either use the provided Unix Makefile:
|
||||
|
||||
make release
|
||||
|
||||
Or compile the source with your preferred compiler:
|
||||
|
||||
cc -O2 -c one.c -o libmujs.o
|
||||
|
||||
INSTALLING
|
||||
|
||||
To install the MuJS command line interpreter, static library and header file:
|
||||
|
||||
make prefix=/usr/local install
|
||||
|
||||
DOWNLOAD
|
||||
|
||||
The latest development source is available directly from the git repository:
|
||||
|
||||
git clone http://git.ghostscript.com/mujs.git
|
||||
|
||||
REPORTING BUGS AND PROBLEMS
|
||||
|
||||
Report bugs on the ghostscript bugzilla, with MuJS as the selected component.
|
||||
|
||||
http://bugs.ghostscript.com/
|
92
src/mujs/astnames.h
Normal file
92
src/mujs/astnames.h
Normal file
|
@ -0,0 +1,92 @@
|
|||
"list",
|
||||
"fundec",
|
||||
"identifier",
|
||||
"exp_identifier",
|
||||
"exp_number",
|
||||
"exp_string",
|
||||
"exp_regexp",
|
||||
"exp_elision",
|
||||
"exp_null",
|
||||
"exp_true",
|
||||
"exp_false",
|
||||
"exp_this",
|
||||
"exp_array",
|
||||
"exp_object",
|
||||
"exp_prop_val",
|
||||
"exp_prop_get",
|
||||
"exp_prop_set",
|
||||
"exp_fun",
|
||||
"exp_index",
|
||||
"exp_member",
|
||||
"exp_call",
|
||||
"exp_new",
|
||||
"exp_postinc",
|
||||
"exp_postdec",
|
||||
"exp_delete",
|
||||
"exp_void",
|
||||
"exp_typeof",
|
||||
"exp_preinc",
|
||||
"exp_predec",
|
||||
"exp_pos",
|
||||
"exp_neg",
|
||||
"exp_bitnot",
|
||||
"exp_lognot",
|
||||
"exp_mod",
|
||||
"exp_div",
|
||||
"exp_mul",
|
||||
"exp_sub",
|
||||
"exp_add",
|
||||
"exp_ushr",
|
||||
"exp_shr",
|
||||
"exp_shl",
|
||||
"exp_in",
|
||||
"exp_instanceof",
|
||||
"exp_ge",
|
||||
"exp_le",
|
||||
"exp_gt",
|
||||
"exp_lt",
|
||||
"exp_strictne",
|
||||
"exp_stricteq",
|
||||
"exp_ne",
|
||||
"exp_eq",
|
||||
"exp_bitand",
|
||||
"exp_bitxor",
|
||||
"exp_bitor",
|
||||
"exp_logand",
|
||||
"exp_logor",
|
||||
"exp_cond",
|
||||
"exp_ass",
|
||||
"exp_ass_mul",
|
||||
"exp_ass_div",
|
||||
"exp_ass_mod",
|
||||
"exp_ass_add",
|
||||
"exp_ass_sub",
|
||||
"exp_ass_shl",
|
||||
"exp_ass_shr",
|
||||
"exp_ass_ushr",
|
||||
"exp_ass_bitand",
|
||||
"exp_ass_bitxor",
|
||||
"exp_ass_bitor",
|
||||
"exp_comma",
|
||||
"exp_var",
|
||||
"stm_block",
|
||||
"stm_empty",
|
||||
"stm_var",
|
||||
"stm_if",
|
||||
"stm_do",
|
||||
"stm_while",
|
||||
"stm_for",
|
||||
"stm_for_var",
|
||||
"stm_for_in",
|
||||
"stm_for_in_var",
|
||||
"stm_continue",
|
||||
"stm_break",
|
||||
"stm_return",
|
||||
"stm_with",
|
||||
"stm_switch",
|
||||
"stm_throw",
|
||||
"stm_try",
|
||||
"stm_debugger",
|
||||
"stm_label",
|
||||
"stm_case",
|
||||
"stm_default",
|
BIN
src/mujs/docs/artifex-logo.png
Executable file
BIN
src/mujs/docs/artifex-logo.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
224
src/mujs/docs/examples.html
Normal file
224
src/mujs/docs/examples.html
Normal file
|
@ -0,0 +1,224 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link href="style.css" rel="stylesheet">
|
||||
<title>MuJS Examples</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<header>
|
||||
<h1>MuJS Examples</h1>
|
||||
</header>
|
||||
|
||||
<nav>
|
||||
<a href="introduction.html">Introduction</a>
|
||||
<a href="reference.html">Reference</a>
|
||||
<a href="examples.html">Examples</a>
|
||||
<a href="license.html">License</a>
|
||||
<a href="http://git.ghostscript.com/?p=mujs.git;a=summary">Source</a>
|
||||
<a href="https://bugs.ghostscript.com/">Bugs</a>
|
||||
</nav>
|
||||
|
||||
<article>
|
||||
|
||||
<h2>A stand-alone interpreter</h2>
|
||||
|
||||
<pre>
|
||||
#include <stdio.h>
|
||||
#include <mujs.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char line[256];
|
||||
js_State *J = js_newstate(NULL, NULL, JS_STRICT);
|
||||
while (fgets(line, sizeof line, stdin))
|
||||
js_dostring(J, line);
|
||||
js_freestate(J);
|
||||
}
|
||||
</pre>
|
||||
|
||||
<h2>Hello, world!</h2>
|
||||
|
||||
<pre>
|
||||
#include <stdio.h>
|
||||
#include <mujs.h>
|
||||
|
||||
static void hello(js_State *J)
|
||||
{
|
||||
const char *name = js_tostring(J, 1);
|
||||
printf("Hello, %s!\n", name);
|
||||
js_pushundefined(J);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
js_State *J = js_newstate(NULL, NULL, JS_STRICT);
|
||||
|
||||
js_newcfunction(J, hello, "hello", 1);
|
||||
js_setglobal(J, "hello");
|
||||
|
||||
js_dostring(J, "hello('world');");
|
||||
|
||||
js_freestate(J);
|
||||
}
|
||||
</pre>
|
||||
|
||||
<h2>Configuration file</h2>
|
||||
|
||||
<pre>
|
||||
js_dofile(J, "config.js")
|
||||
|
||||
js_getglobal(J, "foo");
|
||||
foo = js_tonumber(J, -1);
|
||||
js_pop(J, 1);
|
||||
</pre>
|
||||
|
||||
<h2>Object manipulation</h2>
|
||||
|
||||
<pre>
|
||||
// t = { foo: 42, bar: true }
|
||||
|
||||
js_newobject(J);
|
||||
{
|
||||
js_pushnumber(J, 42);
|
||||
js_setproperty(J, -2, "foo");
|
||||
js_pushboolean(J, 1);
|
||||
js_setproperty(J, -2, "bar");
|
||||
}
|
||||
js_setglobal(J, "t");
|
||||
</pre>
|
||||
|
||||
<h2>Callbacks from C to JS (by name)</h2>
|
||||
|
||||
<pre>
|
||||
static int call_callback(js_State *J, const char *arg1, int arg2)
|
||||
{
|
||||
int result;
|
||||
|
||||
/* Find the function to call. */
|
||||
js_getglobal(J, "my_callback");
|
||||
|
||||
/* Push arguments to function. */
|
||||
js_pushnull(J); /* the 'this' object to use */
|
||||
js_pushstring(J, arg1);
|
||||
js_pushnumber(J, arg2);
|
||||
|
||||
/* Call function and check for exceptions. */
|
||||
if (js_pcall(J, 2)) {
|
||||
fprintf(stderr, "an exception occurred in the javascript callback\n");
|
||||
js_pop(J, 1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Retrieve return value. */
|
||||
result = js_tonumber(J, -1);
|
||||
js_pop(J, 1);
|
||||
|
||||
return result;
|
||||
}
|
||||
</pre>
|
||||
|
||||
<h2>Callbacks from C to JS</h2>
|
||||
|
||||
<pre>
|
||||
const char *handle = NULL; /* handle to stowed away js function */
|
||||
|
||||
static void set_callback(js_State *J)
|
||||
{
|
||||
if (handle)
|
||||
js_unref(J, handle); /* delete old function */
|
||||
js_copy(J, 1);
|
||||
handle = js_ref(J); /* stow the js function in the registry */
|
||||
}
|
||||
|
||||
static void call_callback(js_State *J, int arg1, int arg2)
|
||||
{
|
||||
js_getregistry(J, handle); /* retrieve the js function from the registry */
|
||||
js_pushnull(J);
|
||||
js_pushnumber(J, arg1);
|
||||
js_pushnumber(J, arg2);
|
||||
js_pcall(J, 2);
|
||||
js_pop(J, 1);
|
||||
}
|
||||
</pre>
|
||||
|
||||
<h2>Complete userdata example</h2>
|
||||
|
||||
<pre>
|
||||
#include <stdio.h>
|
||||
#include <mujs.h>
|
||||
|
||||
#define TAG "File"
|
||||
|
||||
static void new_File(js_State *J)
|
||||
{
|
||||
FILE *file;
|
||||
|
||||
if (js_isundefined(J, 1)) {
|
||||
file = stdin;
|
||||
} else {
|
||||
const char *filename = js_tostring(J, 1);
|
||||
file = fopen(filename, "r");
|
||||
if (!file)
|
||||
js_error(J, "cannot open file: '%s'", filename);
|
||||
}
|
||||
|
||||
js_currentfunction(J);
|
||||
js_getproperty(J, -1, "prototype");
|
||||
js_newuserdata(J, TAG, file);
|
||||
}
|
||||
|
||||
static void File_prototype_readByte(js_State *J)
|
||||
{
|
||||
FILE *file = js_touserdata(J, 0, TAG);
|
||||
js_pushnumber(J, getc(file));
|
||||
}
|
||||
|
||||
static void File_prototype_readLine(js_State *J)
|
||||
{
|
||||
char line[256], *s;
|
||||
FILE *file = js_touserdata(J, 0, TAG);
|
||||
s = fgets(line, sizeof line, file);
|
||||
if (s)
|
||||
js_pushstring(J, line);
|
||||
else
|
||||
js_pushnull(J);
|
||||
}
|
||||
|
||||
static void File_prototype_close(js_State *J)
|
||||
{
|
||||
FILE *file = js_touserdata(J, 0, TAG);
|
||||
fclose(file);
|
||||
js_pushundefined(J);
|
||||
}
|
||||
|
||||
void initfile(js_State *J)
|
||||
{
|
||||
js_getglobal(J, "Object");
|
||||
js_getproperty(J, -1, "prototype"); // File.prototype.[[Prototype]] = Object.prototype
|
||||
js_newuserdata(J, TAG, stdin); // File.prototype.[[Userdata]] = stdin
|
||||
{
|
||||
js_newcfunction(J, File_prototype_readByte, "File.prototype.readByte", 0);
|
||||
js_defproperty(J, -2, "readByte", JS_DONTENUM);
|
||||
|
||||
js_newcfunction(J, File_prototype_readLine, "File.prototype.readLine", 0);
|
||||
js_defproperty(J, -2, "readLine", JS_DONTENUM);
|
||||
|
||||
js_newcfunction(J, File_prototype_close, "File.prototype.close", 0);
|
||||
js_defproperty(J, -2, "close", JS_DONTENUM);
|
||||
}
|
||||
js_newcconstructor(J, new_File, new_File, "File", 1);
|
||||
js_defglobal(J, "File", JS_DONTENUM);
|
||||
}
|
||||
</pre>
|
||||
|
||||
</article>
|
||||
|
||||
<footer>
|
||||
<a href="http://artifex.com"><img src="artifex-logo.png" align="right"></a>
|
||||
Copyright © 2013-2017 Artifex Software Inc.
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
58
src/mujs/docs/index.html
Normal file
58
src/mujs/docs/index.html
Normal file
|
@ -0,0 +1,58 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link href="style.css" rel="stylesheet">
|
||||
<title>MuJS</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<header>
|
||||
<h1>MuJS</h1>
|
||||
</header>
|
||||
|
||||
<nav>
|
||||
<a href="introduction.html">Introduction</a>
|
||||
<a href="reference.html">Reference</a>
|
||||
<a href="examples.html">Examples</a>
|
||||
<a href="license.html">License</a>
|
||||
<a href="http://git.ghostscript.com/?p=mujs.git;a=summary">Source</a>
|
||||
<a href="https://bugs.ghostscript.com/">Bugs</a>
|
||||
</nav>
|
||||
|
||||
<article>
|
||||
|
||||
<p>
|
||||
MuJS is a lightweight Javascript interpreter designed for embedding in other
|
||||
software to extend them with scripting capabilities.
|
||||
|
||||
<p>
|
||||
MuJS was designed with a focus on small size, correctness, and simplicity.
|
||||
It is written in portable C and implements ECMAScript as specified by ECMA-262.
|
||||
The interface for binding with native code is designed to be as simple as
|
||||
possible to use, and is very similar to Lua. There is no need to interact with
|
||||
byzantine C++ template mechanisms, or worry about marking and unmarking garbage
|
||||
collection roots, or wrestle with obscure build systems.
|
||||
|
||||
<p>
|
||||
MuJS is developed and maintained by Artifex Software.
|
||||
It was originally developed for use with the MuPDF viewer, but is designed to be useful as an independent component.
|
||||
|
||||
<p>
|
||||
The primary meeting place for the MuJS community is the
|
||||
<a href="http://webchat.freenode.net/?channels=mupdf">#mupdf</a>
|
||||
IRC channel on freenode.
|
||||
|
||||
<p>
|
||||
MuJS is free open source software distributed under the
|
||||
<a href="https://opensource.org/licenses/ISC">ISC license</a>.
|
||||
|
||||
</article>
|
||||
|
||||
<footer>
|
||||
<a href="http://artifex.com"><img src="artifex-logo.png" align="right"></a>
|
||||
Copyright © 2013-2017 Artifex Software Inc.
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
108
src/mujs/docs/introduction.html
Normal file
108
src/mujs/docs/introduction.html
Normal file
|
@ -0,0 +1,108 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link href="style.css" rel="stylesheet">
|
||||
<title>MuJS Introduction</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<header>
|
||||
<h1>MuJS Introduction</h1>
|
||||
</header>
|
||||
|
||||
<nav>
|
||||
<a href="introduction.html">Introduction</a>
|
||||
<a href="reference.html">Reference</a>
|
||||
<a href="examples.html">Examples</a>
|
||||
<a href="license.html">License</a>
|
||||
<a href="http://git.ghostscript.com/?p=mujs.git;a=summary">Source</a>
|
||||
<a href="https://bugs.ghostscript.com/">Bugs</a>
|
||||
</nav>
|
||||
|
||||
<article>
|
||||
|
||||
<h2>Why choose MuJS?</h2>
|
||||
|
||||
<h3>Javascript is a proven scripting language</h3>
|
||||
|
||||
<p>
|
||||
Javascript is one of the most popular programming languages in the world.
|
||||
It is a powerful extension language, used everywhere on the web — both as
|
||||
a way to add interactivity to web pages in the browser, and on the server side
|
||||
with platforms like node.js.
|
||||
|
||||
<p>
|
||||
With MuJS you can bring this power to your application as well!
|
||||
|
||||
<h3>MuJS is standards compliant</h3>
|
||||
|
||||
<p>
|
||||
MuJS implements ES5.
|
||||
There are no non-standard extensions, so you can remain confident that
|
||||
Javascript code that runs on MuJS will also run on any other standards
|
||||
compliant Javascript implementation.
|
||||
|
||||
<h3>MuJS is portable</h3>
|
||||
|
||||
<p>
|
||||
MuJS is written in portable C and can be built by compiling a single C file using any standard C compiler.
|
||||
There is no need for configuration or fancy build systems.
|
||||
MuJS runs on all flavors of Unix and Windows, on mobile devices (such as Android and iOS),
|
||||
embedded microprocessors (such as the Beagle board and Raspberry Pi), etc.
|
||||
|
||||
<h3>MuJS is embeddable</h3>
|
||||
|
||||
<p>
|
||||
MuJS is a simple language engine with a small footprint that you can easily embed into your application.
|
||||
The API is simple and well documented and allows strong integration with code written in other languages.
|
||||
You don't need to work with byzantine C++ templating mechanisms, or manually manage garbage collection roots.
|
||||
It is easy to extend MuJS with libraries written in other languages.
|
||||
It is also easy to extend programs written in other languages with MuJS.
|
||||
|
||||
<h3>MuJS is small</h3>
|
||||
|
||||
<p>
|
||||
Adding MuJS to an application does not bloat it.
|
||||
The source contains around 15'000 lines of C.
|
||||
Under 64-bit Linux, the compiled library takes 180kB if optimized for size,
|
||||
and 260kB if optimized for speed.
|
||||
|
||||
Compare this with V8, SpiderMonkey or JavaScriptCore,
|
||||
which are all several hundred thousand lines of code,
|
||||
take several megabytes of space,
|
||||
and require the C++ runtime.
|
||||
|
||||
<h3>MuJS is reasonably fast and secure</h3>
|
||||
|
||||
<p>
|
||||
It is a bytecode interpreter with a very fast mechanism to call-out to C.
|
||||
The default build is sandboxed with very restricted access to resources.
|
||||
Due to the nature of bytecode, MuJS is not as fast as JIT compiling
|
||||
implementations but starts up faster and uses fewer resources.
|
||||
If you implement heavy lifting in C code, controlled by Javascript,
|
||||
you can get the best of both worlds.
|
||||
|
||||
<h3>MuJS is free software</h3>
|
||||
|
||||
<p>
|
||||
MuJS is free open source software distributed under the
|
||||
<a href="https://opensource.org/licenses/ISC">ISC license</a>.
|
||||
|
||||
<h3>MuJS is developed by a stable company</h3>
|
||||
|
||||
<p>
|
||||
<a href="http://artifex.com/">Artifex Software</a> has long experience in
|
||||
interpreters and page description languages, and has a history with open source
|
||||
that goes back to 1993 when it was created to facilitate licensing Ghostscript
|
||||
to OEMs.
|
||||
|
||||
</article>
|
||||
|
||||
<footer>
|
||||
<a href="http://artifex.com"><img src="artifex-logo.png" align="right"></a>
|
||||
Copyright © 2013-2017 Artifex Software Inc.
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
50
src/mujs/docs/license.html
Normal file
50
src/mujs/docs/license.html
Normal file
|
@ -0,0 +1,50 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link href="style.css" rel="stylesheet">
|
||||
<title>MuJS License</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<header>
|
||||
<h1>MuJS License</h1>
|
||||
</header>
|
||||
|
||||
<nav>
|
||||
<a href="introduction.html">Introduction</a>
|
||||
<a href="reference.html">Reference</a>
|
||||
<a href="examples.html">Examples</a>
|
||||
<a href="license.html">License</a>
|
||||
<a href="http://git.ghostscript.com/?p=mujs.git;a=summary">Source</a>
|
||||
<a href="https://bugs.ghostscript.com/">Bugs</a>
|
||||
</nav>
|
||||
|
||||
<article>
|
||||
|
||||
<p>
|
||||
MuJS is Copyright © 2013-2017 Artifex Software, Inc.
|
||||
|
||||
<p>
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
<p>
|
||||
The software is provided "as is" and the author disclaims all warranties with
|
||||
regard to this software including all implied warranties of merchantability and
|
||||
fitness. In no event shall the author be liable for any special, direct,
|
||||
indirect, or consequential damages or any damages whatsoever resulting from
|
||||
loss of use, data or profits, whether in an action of contract, negligence or
|
||||
other tortious action, arising out of or in connection with the use or
|
||||
performance of this software.
|
||||
|
||||
</article>
|
||||
|
||||
<footer>
|
||||
<a href="http://artifex.com"><img src="artifex-logo.png" align="right"></a>
|
||||
Copyright © 2013-2017 Artifex Software Inc.
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
47
src/mujs/docs/logo.ps
Normal file
47
src/mujs/docs/logo.ps
Normal file
|
@ -0,0 +1,47 @@
|
|||
%!
|
||||
<</PageSize[512 512]>>setpagedevice
|
||||
|
||||
% #323330 = 50 51 48
|
||||
% #F0DB4F = 240 219 79
|
||||
% #4386b5 = 67 134 181
|
||||
|
||||
/cG { 50 255 div 51 255 div 48 255 div setrgbcolor } def
|
||||
/cY { 240 255 div 219 255 div 79 255 div setrgbcolor } def
|
||||
/cB { 67 255 div 134 255 div 181 255 div setrgbcolor } def
|
||||
|
||||
% fill background with yellow
|
||||
cY
|
||||
0 0 moveto 512 0 lineto 512 512 lineto 0 512 lineto closepath fill
|
||||
|
||||
% move logo to lower right corner
|
||||
512 0.2 mul 0 translate
|
||||
0.8 0.8 scale
|
||||
|
||||
% center logo
|
||||
0.875 0.875 scale
|
||||
32 32 translate
|
||||
|
||||
% draw electrons and nucleus
|
||||
cG
|
||||
gsave
|
||||
256 256 translate
|
||||
|
||||
16 setlinewidth
|
||||
gsave 0 rotate .5 1 scale 0 0 232 0 360 arc stroke grestore
|
||||
gsave 60 rotate .5 1 scale 0 0 232 0 360 arc stroke grestore
|
||||
gsave 120 rotate .5 1 scale 0 0 232 0 360 arc stroke grestore
|
||||
|
||||
0 0 96 0 360 arc fill
|
||||
grestore
|
||||
|
||||
% draw yellow 'JS' text in center of nucleus
|
||||
cY
|
||||
gsave
|
||||
/SourceSansPro-Bold findfont 128 scalefont setfont
|
||||
256 256 moveto
|
||||
(JS)
|
||||
dup stringwidth pop -2 div -44 rmoveto
|
||||
show
|
||||
grestore
|
||||
|
||||
showpage
|
BIN
src/mujs/docs/mujs-logo.png
Normal file
BIN
src/mujs/docs/mujs-logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.9 KiB |
719
src/mujs/docs/reference.html
Normal file
719
src/mujs/docs/reference.html
Normal file
|
@ -0,0 +1,719 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link href="style.css" rel="stylesheet">
|
||||
<title>MuJS Reference</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<header>
|
||||
<h1>MuJS Reference</h1>
|
||||
</header>
|
||||
|
||||
<nav>
|
||||
<a href="introduction.html">Introduction</a>
|
||||
<a href="reference.html">Reference</a>
|
||||
<a href="examples.html">Examples</a>
|
||||
<a href="license.html">License</a>
|
||||
<a href="http://git.ghostscript.com/?p=mujs.git;a=summary">Source</a>
|
||||
<a href="https://bugs.ghostscript.com/">Bugs</a>
|
||||
</nav>
|
||||
|
||||
<article>
|
||||
|
||||
<h2>Introduction</h2>
|
||||
|
||||
<p>
|
||||
MuJS is a library, written in clean and simple C.
|
||||
Being an extension library, MuJS has no notion of a main program: it only works embedded in a host client program.
|
||||
The host program can invoke functions to execute Javascript code, read and write Javascript variables, and register C functions to be called by Javascript.
|
||||
|
||||
<p>
|
||||
The MuJS distribution includes a sample host program called "mujs", which uses the MuJS library to offer a standalone Javascript interpreter for interactive or batch use.
|
||||
|
||||
<p>
|
||||
This reference manual assumes that you are already familiar with the Javascript language, in particular the type system and object prototype mechanisms.
|
||||
|
||||
<h2>Basic Concepts</h2>
|
||||
|
||||
<h3>Values and Types</h3>
|
||||
|
||||
<p>
|
||||
There are six basic types in Javascript: undefined, null, boolean, number, string and object.
|
||||
|
||||
<p>
|
||||
Each object also has a class: object, array, function, userdata, regular expression, etc.
|
||||
|
||||
<p>
|
||||
Javascript can call functions written in C provided by the host program, as well as other Javascript functions.
|
||||
|
||||
<p>
|
||||
Objects with the userdata class are provided to allow arbitrary C data to be attached to Javascript objects.
|
||||
A userdata object has a pointer to a block of raw memory, which is managed by the host.
|
||||
Userdata values cannot be created or modified in Javascript, only through the C API.
|
||||
This guarantees the integrity of data owned by the host program.
|
||||
|
||||
<p>
|
||||
Custom properties on userdata objects can be implemented using getter and setter property accessor functions.
|
||||
|
||||
<p>
|
||||
Numbers are represented using double precision floating point values.
|
||||
|
||||
<p>
|
||||
Strings in the C interface are zero-terminated byte arrays in WTF-8 encoding.
|
||||
This allows both arbitrary 16-bit values (as required by Javascript) and also
|
||||
extended code points for the full 21-bit Unicode range.
|
||||
These extended characters will mostly work as expected in Javascript.
|
||||
|
||||
<p>
|
||||
If you have Javascript code that expects to work with UTF-16 surrogate pairs,
|
||||
you will need to manually convert any extended characters to surrogate pairs
|
||||
and back when passing strings between C and Javascript.
|
||||
|
||||
<p>
|
||||
The U+0000 character is encoded as the two-byte sequence <C0 80>, same as in
|
||||
modified UTF-8.
|
||||
|
||||
<h3>Environments</h3>
|
||||
|
||||
<p>
|
||||
Each function executes within an environment which defines which variables are accessible.
|
||||
This is a chain of all environment records in scope, with the global environment at the top.
|
||||
Each environment record in MuJS is represented as an object with the null prototype, including the global environment object.
|
||||
|
||||
<p>
|
||||
The registry is a hidden environment record which is only accessible to C.
|
||||
This is where Javascript values and objects that should only be accessible to C functions may be stored.
|
||||
|
||||
<h3>Error Handling</h3>
|
||||
|
||||
<p>
|
||||
All Javascript actions start from C code in the host program calling a function from the MuJS library.
|
||||
Whenever an exception is thrown during the compilation or execution of Javascript, control returns to the host, which can take appropriate measures (such as printing an error message).
|
||||
C code can also throw exceptions by calling functions to create an error object and return control to Javascript.
|
||||
|
||||
<p>
|
||||
Internally, MuJS uses the C longjmp facility to handle errors.
|
||||
A protected environment uses setjmp to set a recovery point.
|
||||
The try statement in Javascript creates such a recovery point, as does calling js_dostring, js_dofile, js_ploadstring, js_ploadfile,
|
||||
js_pcall and js_pconstruct.
|
||||
|
||||
<p>
|
||||
When an error occurs or an exception is thrown from Javascript, it does a long jump to the most recent active recovery point.
|
||||
|
||||
<p>
|
||||
If an error occurs outside any protected environment, MuJS first calls the panic function and then calls abort, thus exiting the host application.
|
||||
Your panic function can avoid this exit by never returning (for example by doing a long jump to your own recovery point outside MuJS).
|
||||
|
||||
<h3>Garbage Collection</h3>
|
||||
|
||||
<p>
|
||||
MuJS performs automatic memory management using a basic mark-and-sweep collector.
|
||||
Collection is automatically triggered when enough allocations have accumulated.
|
||||
You can also force a collection pass from C.
|
||||
|
||||
<p>
|
||||
Userdata objects have an associated C finalizer function that is called when
|
||||
the corresponding object is freed.
|
||||
|
||||
<h3>The Stack</h3>
|
||||
|
||||
<p>
|
||||
MuJS uses a virtual stack to pass values to and from C.
|
||||
Each element in this stack represents a Javascript value (null, number, string, etc).
|
||||
|
||||
<p>
|
||||
Whenever Javascript calls C, the called function gets a new stack.
|
||||
This stack initially contains the this value and any arguments passed to the function.
|
||||
When the C function returns, the top value on the stack is passed back to the caller as the return value.
|
||||
|
||||
<p>
|
||||
The stack values are accessed using stack indices.
|
||||
Index 0 always contains the this value, and function arguments are index 1 and up.
|
||||
Negative indices count down from the top of the stack, so index -1 is the top of the index and index -2 is the one below that.
|
||||
|
||||
<h2>The Application Program Interface</h2>
|
||||
|
||||
<h3>State</h3>
|
||||
|
||||
<pre>
|
||||
typedef struct js_State js_State;
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
The interpreter state is bundled up in the opaque struct js_State.
|
||||
This state contains the value stacks, protected environments, and environment records.
|
||||
|
||||
<pre>
|
||||
js_State *js_newstate(js_Alloc alloc, void *context, int flags);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Create a new state using the allocator function and allocator context.
|
||||
Pass NULL to use the default allocator.
|
||||
|
||||
<p>
|
||||
The available flags:
|
||||
|
||||
<ul>
|
||||
<li>JS_STRICT: compile and run code using ES5 strict mode.
|
||||
</ul>
|
||||
|
||||
<pre>
|
||||
void js_freestate(js_State *J);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Destroy the state and free all dynamic memory used by the state.
|
||||
|
||||
<h3>Allocator</h3>
|
||||
|
||||
<p>
|
||||
The interpreter uses a host provided function for all memory allocation needs:
|
||||
|
||||
<pre>
|
||||
typedef void *(*js_Alloc)(void *memctx, void *ptr, int size);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
When size is zero, the allocator should behave like free and return NULL.
|
||||
When size is not zero, the allocator should behave like realloc.
|
||||
The allocator should return NULL if it cannot fulfill the request.
|
||||
The default allocator uses malloc, realloc and free.
|
||||
|
||||
<h3>Panic</h3>
|
||||
|
||||
<pre>
|
||||
typedef void (*js_Panic)(js_State *J);
|
||||
|
||||
js_Panic js_atpanic(js_State *J, js_Panic panic);
|
||||
</pre>
|
||||
|
||||
Set a new panic function, and return the old one.
|
||||
|
||||
<h3>Report</h3>
|
||||
|
||||
<pre>
|
||||
typedef void (*js_Report)(js_State *J, const char *message);
|
||||
|
||||
void js_setreport(js_State *J, js_Report report);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Set a callback function for reporting various warnings
|
||||
and garbage collection statistics.
|
||||
|
||||
<p>
|
||||
The report function must <i>not</i> throw an exception
|
||||
or call any other MuJS function except js_getcontext().
|
||||
|
||||
<h3>Garbage collection</h3>
|
||||
|
||||
<pre>
|
||||
js_gc(js_State *J, int report);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Force a garbage collection pass.
|
||||
If the report argument is non-zero, send a summary of garbage collection statistics to
|
||||
the report callback function.
|
||||
|
||||
<h3>Loading and compiling scripts</h3>
|
||||
|
||||
<p>
|
||||
A script is compiled by calling js_loadstring or js_loadfile.
|
||||
The result of a successful compilation is a function on the top of the stack.
|
||||
This function can then be executed with js_call.
|
||||
|
||||
<pre>
|
||||
void js_loadstring(js_State *J, const char *filename, const char *source);
|
||||
void js_loadfile(js_State *J, const char *filename);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Compile the script and push the resulting function.
|
||||
|
||||
<pre>
|
||||
int js_ploadstring(js_State *J, const char *filename, const char *source);
|
||||
int js_ploadfile(js_State *J, const char *filename);
|
||||
</pre>
|
||||
|
||||
Like js_loadstring/js_loadfile but in a protected environment.
|
||||
In case of success, return 0 with the result as a function on the stack.
|
||||
In case of failure, return 1 with the error object on the stack.
|
||||
|
||||
<h3>Calling functions</h3>
|
||||
|
||||
<pre>
|
||||
void js_call(js_State *J, int n);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
To call a function, you must use the following protocol:
|
||||
1) push the function to call onto the stack,
|
||||
2) push the this value to be used by the function,
|
||||
3) push the arguments to the function in order,
|
||||
4) finally, call js_call with the number of arguments pushed in step 3.
|
||||
|
||||
<p>
|
||||
Pop the function, the this value, and all arguments;
|
||||
execute the function;
|
||||
then push the return value from the function.
|
||||
|
||||
<pre>
|
||||
void js_construct(js_State *J, int n);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
The construct function implements the 'new' expression in Javascript.
|
||||
This is similar to js_call, but without pushing a this value:
|
||||
1) push the constructor function to call onto the stack,
|
||||
2) push the arguments to the constructor function in order,
|
||||
3) finally, call js_construct with the number of arguments pushed in step 2.
|
||||
|
||||
<pre>
|
||||
int js_pcall(js_State *J, int n);
|
||||
int js_pconstruct(js_State *J, int n);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Like js_call and js_construct but in a protected environment.
|
||||
In case of success, return 0 with the result on the stack.
|
||||
In case of failure, return 1 with the error object on the stack.
|
||||
|
||||
<h3>Script helpers</h3>
|
||||
|
||||
<p>
|
||||
There are two convenience functions for loading and executing code.
|
||||
|
||||
<pre>
|
||||
int js_dostring(js_State *J, const char *source);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Compile and execute the script in the zero-terminated string in source argument.
|
||||
If any errors occur, call the report callback function and return 1.
|
||||
Return 0 on success.
|
||||
|
||||
<pre>
|
||||
int js_dofile(js_State *J, const char *filename);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Load the script from the file with the given filename, then compile and execute it.
|
||||
If any errors occur, call the report callback function and return 1.
|
||||
Return 0 on success.
|
||||
|
||||
<h3>Protected environments</h3>
|
||||
|
||||
<p>
|
||||
The js_try macro pushes a new protected environment and calls setjmp.
|
||||
If it returns true, an error has occurred. The protected environment has been popped
|
||||
and the error object is located on the top of the stack.
|
||||
|
||||
<p>
|
||||
At the end of the code you want to run in the protected environment you must call
|
||||
js_endtry in order to pop the protected environment. Note: you should <i>not</i> call
|
||||
js_endtry when an error has occurred and you are in the true-branch of js_try.
|
||||
|
||||
<p>
|
||||
Since the macro is a wrapper around setjmp, the usual
|
||||
<a href="http://pubs.opengroup.org/onlinepubs/007908799/xsh/setjmp.html">restrictions</a> apply.
|
||||
Use the following example as a guide for how to use js_try:
|
||||
|
||||
<pre>
|
||||
if (js_try(J)) {
|
||||
fprintf(stderr, "error: %s", js_trystring(J, -1, "Error"));
|
||||
js_pop(J, 1);
|
||||
return;
|
||||
}
|
||||
do_some_stuff();
|
||||
js_endtry(J);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Most of the time you shouldn't need to worry about protected environments.
|
||||
The functions prefixed with 'p' (js_pcall, js_ploadstring, etc) handle setting
|
||||
up the protected environment and return simple error codes.
|
||||
|
||||
<h3>Errors</h3>
|
||||
|
||||
<pre>
|
||||
void js_throw(js_State *J);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Pop the error object on the top of the stack and return control flow to the most recent protected environment.
|
||||
|
||||
<pre>
|
||||
void js_newerror(js_State *J, const char *message);
|
||||
void js_newevalerror(js_State *J, const char *message);
|
||||
void js_newrangeerror(js_State *J, const char *message);
|
||||
void js_newreferenceerror(js_State *J, const char *message);
|
||||
void js_newsyntaxerror(js_State *J, const char *message);
|
||||
void js_newtypeerror(js_State *J, const char *message);
|
||||
void js_newurierror(js_State *J, const char *message);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Push a new error object on the stack.
|
||||
|
||||
<pre>
|
||||
void js_error(js_State *J, const char *fmt, ...);
|
||||
void js_evalerror(js_State *J, const char *fmt, ...);
|
||||
void js_rangeerror(js_State *J, const char *fmt, ...);
|
||||
void js_referenceerror(js_State *J, const char *fmt, ...);
|
||||
void js_syntaxerror(js_State *J, const char *fmt, ...);
|
||||
void js_typeerror(js_State *J, const char *fmt, ...);
|
||||
void js_urierror(js_State *J, const char *fmt, ...);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Wrapper to push a new error object on the stack using a printf formatting string and call js_throw.
|
||||
|
||||
<h3>Stack manipulation</h3>
|
||||
|
||||
<pre>
|
||||
int js_gettop(js_State *J);
|
||||
void js_pop(js_State *J, int n);
|
||||
void js_rot(js_State *J, int n);
|
||||
void js_copy(js_State *J, int idx);
|
||||
void js_remove(js_State *J, int idx);
|
||||
void js_insert(js_State *J, int idx);
|
||||
void js_replace(js_State* J, int idx);
|
||||
</pre>
|
||||
|
||||
<h3>Comparisons and arithmetic</h3>
|
||||
|
||||
<pre>
|
||||
void js_concat(js_State *J);
|
||||
int js_compare(js_State *J, int *okay);
|
||||
int js_equal(js_State *J);
|
||||
int js_strictequal(js_State *J);
|
||||
int js_instanceof(js_State *J);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
The equivalent of the '+', comparison, and instanceof operators.
|
||||
The okay argument to js_compare is set to 0 if any of the values are NaN, otherwise it is set to 1.
|
||||
|
||||
</pre>
|
||||
|
||||
<h3>Primitive values</h3>
|
||||
|
||||
<pre>
|
||||
void js_pushundefined(js_State *J);
|
||||
void js_pushnull(js_State *J);
|
||||
void js_pushboolean(js_State *J, int v);
|
||||
void js_pushnumber(js_State *J, double v);
|
||||
void js_pushstring(js_State *J, const char *v);
|
||||
void js_pushliteral(js_State *J, const char *v);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Push primitive values.
|
||||
js_pushstring makes a copy of the string, so it may be freed or changed after passing it in.
|
||||
js_pushliteral keeps a pointer to the string, so it must not be changed or freed after passing it in.
|
||||
|
||||
<pre>
|
||||
int js_isdefined(js_State *J, int idx);
|
||||
int js_isundefined(js_State *J, int idx);
|
||||
int js_isnull(js_State *J, int idx);
|
||||
int js_isboolean(js_State *J, int idx);
|
||||
int js_isnumber(js_State *J, int idx);
|
||||
int js_isstring(js_State *J, int idx);
|
||||
int js_isprimitive(js_State *J, int idx);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Test if a primitive value is of a given type.
|
||||
|
||||
<pre>
|
||||
int js_toboolean(js_State *J, int idx);
|
||||
double js_tonumber(js_State *J, int idx);
|
||||
int js_tointeger(js_State *J, int idx);
|
||||
int js_toint32(js_State *J, int idx);
|
||||
unsigned int js_touint32(js_State *J, int idx);
|
||||
short js_toint16(js_State *J, int idx);
|
||||
unsigned short js_touint16(js_State *J, int idx);
|
||||
const char *js_tostring(js_State *J, int idx);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Convert the value at the given index into a C value.
|
||||
If the value is an object, invoke the toString and/or valueOf methods to do the conversion.
|
||||
|
||||
<p>
|
||||
The conversion may <i>change the actual value in the stack</i>!
|
||||
|
||||
<p>
|
||||
There is no guarantee that the pointer returned by js_tostring will be valid after
|
||||
the corresponding value is removed from the stack.
|
||||
|
||||
<p>
|
||||
Note that the toString and valueOf methods that may be invoked by these functions
|
||||
can throw exceptions. If you want to catch and ignore exceptions, use the following
|
||||
functions instead. The 'error' argument is the default value that will be returned
|
||||
if a toString/valueOf method throws an exception.
|
||||
|
||||
<pre>
|
||||
int js_tryboolean(js_State *J, int idx, int error);
|
||||
double js_trynumber(js_State *J, int idx, double error);
|
||||
int js_tryinteger(js_State *J, int idx, int error);
|
||||
const char *js_trystring(js_State *J, int idx, const char *error);
|
||||
</pre>
|
||||
|
||||
<h3>Objects</h3>
|
||||
|
||||
<pre>
|
||||
enum {
|
||||
JS_REGEXP_G = 1,
|
||||
JS_REGEXP_I = 2,
|
||||
JS_REGEXP_M = 4,
|
||||
};
|
||||
|
||||
void js_newobject(js_State *J);
|
||||
void js_newarray(js_State *J);
|
||||
void js_newboolean(js_State *J, int v);
|
||||
void js_newnumber(js_State *J, double v);
|
||||
void js_newstring(js_State *J, const char *v);
|
||||
void js_newregexp(js_State *J, const char *pattern, int flags);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Create and push objects on the stack.
|
||||
|
||||
<pre>
|
||||
int js_isobject(js_State *J, int idx);
|
||||
int js_isarray(js_State *J, int idx);
|
||||
int js_iscallable(js_State *J, int idx);
|
||||
int js_isregexp(js_State *J, int idx);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Test the type and class of an object on the stack.
|
||||
|
||||
<h3>Properties</h3>
|
||||
|
||||
<p>
|
||||
The property functions all work on an object.
|
||||
If the stack slot referenced by the index does not contain an object, they will throw an error.
|
||||
|
||||
<pre>
|
||||
enum {
|
||||
JS_READONLY = 1,
|
||||
JS_DONTENUM = 2,
|
||||
JS_DONTCONF = 4,
|
||||
};
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Property attribute bit-mask values.
|
||||
|
||||
<pre>
|
||||
int js_hasproperty(js_State *J, int idx, const char *name);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
If the object has a property with the given name, return 1 and push the value of the property; otherwise return 0 and leave the stack untouched.
|
||||
|
||||
<pre>
|
||||
void js_getproperty(js_State *J, int idx, const char *name);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Push the value of the named property of the object.
|
||||
If the object does not have the named property, push undefined instead.
|
||||
|
||||
<pre>
|
||||
void js_setproperty(js_State *J, int idx, const char *name);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Pop a value from the top of the stack and set the value of the named property of the object.
|
||||
|
||||
<pre>
|
||||
void js_defproperty(js_State *J, int idx, const char *name, int atts);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Pop a value from the top of the stack and set the value of the named property of the object.
|
||||
Also define the property attributes.
|
||||
|
||||
<pre>
|
||||
void js_defaccessor(js_State *J, int idx, const char *name, int atts);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Define the getter and setter attributes of a property on the object.
|
||||
Pop the two getter and setter functions from the stack.
|
||||
Use null instead of a function object if you want to leave any of the functions unset.
|
||||
|
||||
<pre>
|
||||
void js_delproperty(js_State *J, int idx, const char *name);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Delete the named property from the object.
|
||||
|
||||
<h3>Array properties</h3>
|
||||
|
||||
<pre>
|
||||
int js_getlength(js_State *J, int idx);
|
||||
void js_setlength(js_State *J, int idx, int len);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Wrappers to get and set the "length" property of an object.
|
||||
|
||||
<pre>
|
||||
int js_hasindex(js_State *J, int idx, int i);
|
||||
void js_getindex(js_State *J, int idx, int i);
|
||||
void js_setindex(js_State *J, int idx, int i);
|
||||
void js_delindex(js_State *J, int idx, int i);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
These array index functions functions are simple wrappers around the equivalent property functions.
|
||||
They convert the numeric index to a string to use as the property name.
|
||||
|
||||
<h3>Globals</h3>
|
||||
|
||||
<pre>
|
||||
void js_pushglobal(js_State *J);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Push the object representing the global environment record.
|
||||
|
||||
<pre>
|
||||
void js_getglobal(js_State *J, const char *name);
|
||||
void js_setglobal(js_State *J, const char *name);
|
||||
void js_defglobal(js_State *J, const char *name, int atts);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Wrappers around js_pushglobal and js_get/set/defproperty to read and write the values of global variables.
|
||||
|
||||
<h3>C Functions</h3>
|
||||
|
||||
<pre>
|
||||
void js_newcfunction(js_State *J, js_CFunction fun, const char *name, int length);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Push a function object wrapping a C function pointer.
|
||||
|
||||
<p>
|
||||
The length argument is the number of arguments to the function.
|
||||
If the function is called with fewer arguments, the argument list will be padded with undefined.
|
||||
|
||||
<pre>
|
||||
void js_newcconstructor(js_State *J,
|
||||
js_CFunction fun, js_CFunction con,
|
||||
const char *name, int length);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Pop the object to set as the "prototype" property for the constructor function object.
|
||||
Push a function object wrapping a C function pointer, allowing for separate function pointers for regular calls and 'new' operator calls.
|
||||
|
||||
<pre>
|
||||
void js_currentfunction(js_State *J);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Push the currently executing function object.
|
||||
|
||||
<h3>Userdata</h3>
|
||||
|
||||
<pre>
|
||||
typedef void (*js_Finalize)(js_State *J, void *data);
|
||||
typedef int (*js_HasProperty)(js_State *J, void *data, const char *name);
|
||||
typedef int (*js_Put)(js_State *J, void *data, const char *name);
|
||||
typedef int (*js_Delete)(js_State *J, void *data, const char *name);
|
||||
|
||||
void js_newuserdata(js_State *J, const char *tag, void *data,
|
||||
js_Finalize finalize);
|
||||
|
||||
void js_newuserdatax(js_State *J, const char *tag, void *data,
|
||||
js_HasProperty has,
|
||||
js_Put put,
|
||||
js_Delete delete,
|
||||
js_Finalize finalize);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Pop an object from the top of the stack to use as the internal prototype property for the new object.
|
||||
Push a new userdata object wrapping a pointer to C memory.
|
||||
The userdata object is tagged using a string, to represent the type of the C memory.
|
||||
|
||||
<p>
|
||||
The finalize callback, if it is not NULL, will be called when the object is
|
||||
freed by the garbage collector.
|
||||
|
||||
<p>
|
||||
The extended function also has callback functions for overriding property accesses.
|
||||
If these are set, they can be used to override accesses to certain properties.
|
||||
Any property accesses that are not overridden will be handled as usual in the runtime.
|
||||
The "HasProperty" callback should push a value and return true if it wants to
|
||||
handle the property, otherwise it should do nothing and return false. "Put"
|
||||
should read the top value and return true if it wants to handle the property.
|
||||
Likewise, "Delete" should return true if it wants to handle the property.
|
||||
|
||||
<pre>
|
||||
int js_isuserdata(js_State *J, int idx, const char *tag);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Test if an object is a userdata object with the given type tag string.
|
||||
|
||||
<pre>
|
||||
void *js_touserdata(js_State *J, int idx, const char *tag);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Return the wrapped pointer from a userdata object.
|
||||
If the object is undefined or null, return NULL.
|
||||
If the object is not a userdata object with the given type tag string, throw a type error.
|
||||
|
||||
<h3>Registry</h3>
|
||||
|
||||
<p>
|
||||
The registry can be used to store references to Javascript objects accessible from C,
|
||||
but hidden from Javascript to prevent tampering.
|
||||
|
||||
<pre>
|
||||
void js_getregistry(js_State *J, const char *name);
|
||||
void js_setregistry(js_State *J, const char *name);
|
||||
void js_delregistry(js_State *J, const char *name);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Access properties on the hidden registry object.
|
||||
|
||||
<pre>
|
||||
const char *js_ref(js_State *J);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
WIP: Pop a value from the stack and store it in the registry using a new unique property name.
|
||||
Return the property name.
|
||||
|
||||
<pre>
|
||||
void js_unref(js_State *J, const char *ref);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
WIP: Delete the reference from the registry.
|
||||
|
||||
</article>
|
||||
|
||||
<footer>
|
||||
<a href="http://artifex.com"><img src="artifex-logo.png" align="right"></a>
|
||||
Copyright © 2013-2017 Artifex Software Inc.
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
40
src/mujs/docs/style.css
Normal file
40
src/mujs/docs/style.css
Normal file
|
@ -0,0 +1,40 @@
|
|||
h1, nav, footer { font-family: sans-serif; }
|
||||
|
||||
a { text-decoration: none; }
|
||||
a:hover { text-decoration: underline; }
|
||||
h2 { font-size: 1.25rem; }
|
||||
h3 { font-size: 1.12rem; }
|
||||
ul li { list-style-type: circle; }
|
||||
pre, table, ol, dl { margin-left: 2rem; }
|
||||
li { margin: 0; }
|
||||
th, td { text-align: left; vertical-align: top; }
|
||||
|
||||
body { margin: 0; }
|
||||
h1 {
|
||||
font-weight: normal;
|
||||
margin: 0;
|
||||
padding: 1rem 2rem;
|
||||
}
|
||||
header{
|
||||
color: white;
|
||||
background: no-repeat;
|
||||
background-color: #36648b;
|
||||
background-image: url("mujs-logo.png");
|
||||
background-position: top right;
|
||||
min-height: 72px;
|
||||
}
|
||||
nav {
|
||||
padding: 0.75rem 2rem;
|
||||
background-color: #ddd;
|
||||
no-text-transform: uppercase;
|
||||
}
|
||||
nav a { color: #303030; padding-right: 2rem; }
|
||||
article {
|
||||
max-width: 50rem;
|
||||
margin: 2rem;
|
||||
}
|
||||
footer {
|
||||
background-color: #ddd;
|
||||
color: #303030;
|
||||
padding: 1rem 2rem;
|
||||
}
|
117
src/mujs/genucd.py
Normal file
117
src/mujs/genucd.py
Normal file
|
@ -0,0 +1,117 @@
|
|||
# Create utfdata.h from UnicodeData.txt and SpecialCasing.txt
|
||||
|
||||
import sys
|
||||
|
||||
tolower = []
|
||||
toupper = []
|
||||
tolower_full = []
|
||||
toupper_full = []
|
||||
isalpha = []
|
||||
|
||||
for line in open(sys.argv[1]).readlines():
|
||||
line = line.split(";")
|
||||
code = int(line[0],16)
|
||||
# if code > 65535: continue # skip non-BMP codepoints
|
||||
if line[2][0] == 'L':
|
||||
isalpha.append(code)
|
||||
if line[12]:
|
||||
toupper.append((code,int(line[12],16)))
|
||||
if line[13]:
|
||||
tolower.append((code,int(line[13],16)))
|
||||
|
||||
for line in open(sys.argv[2]).readlines():
|
||||
# SpecialCasing.txt -- code; lower; title; upper; (condition;)? # comment
|
||||
line = line.strip()
|
||||
if len(line) == 0:
|
||||
continue
|
||||
if line[0] == "#":
|
||||
continue
|
||||
line = line.split(";")
|
||||
code = int(line[0],16)
|
||||
lower = line[1].strip()
|
||||
upper = line[3].strip()
|
||||
if len(lower) == 0 or len(upper) == 0:
|
||||
continue
|
||||
condition = line[4].split("#")[0].strip()
|
||||
if len(condition) > 0:
|
||||
continue
|
||||
lower = list(map(lambda x: int(x,16), lower.split(" ")))
|
||||
upper = list(map(lambda x: int(x,16), upper.split(" ")))
|
||||
if lower[0] != code:
|
||||
tolower_full.append([code] + lower)
|
||||
if upper[0] != code:
|
||||
toupper_full.append([code] + upper)
|
||||
|
||||
tolower_full.sort()
|
||||
toupper_full.sort()
|
||||
|
||||
def dumpalpha():
|
||||
table = []
|
||||
prev = 0
|
||||
start = 0
|
||||
for code in isalpha:
|
||||
if code != prev+1:
|
||||
if start:
|
||||
table.append((start,prev))
|
||||
start = code
|
||||
prev = code
|
||||
table.append((start,prev))
|
||||
|
||||
print("")
|
||||
print("static const Rune ucd_alpha2[] = {")
|
||||
for a, b in table:
|
||||
if b - a > 0:
|
||||
print(hex(a)+","+hex(b)+",")
|
||||
print("};");
|
||||
|
||||
print("")
|
||||
print("static const Rune ucd_alpha1[] = {")
|
||||
for a, b in table:
|
||||
if b - a == 0:
|
||||
print(hex(a)+",")
|
||||
print("};");
|
||||
|
||||
def dumpmap(name, input):
|
||||
table = []
|
||||
prev_a = 0
|
||||
prev_b = 0
|
||||
start_a = 0
|
||||
start_b = 0
|
||||
for a, b in input:
|
||||
if a != prev_a+1 or b != prev_b+1:
|
||||
if start_a:
|
||||
table.append((start_a,prev_a,start_b))
|
||||
start_a = a
|
||||
start_b = b
|
||||
prev_a = a
|
||||
prev_b = b
|
||||
table.append((start_a,prev_a,start_b))
|
||||
|
||||
print("")
|
||||
print("static const Rune " + name + "2[] = {")
|
||||
for a, b, n in table:
|
||||
if b - a > 0:
|
||||
print(hex(a)+","+hex(b)+","+str(n-a)+",")
|
||||
print("};");
|
||||
|
||||
print("")
|
||||
print("static const Rune " + name + "1[] = {")
|
||||
for a, b, n in table:
|
||||
if b - a == 0:
|
||||
print(hex(a)+","+str(n-a)+",")
|
||||
print("};");
|
||||
|
||||
def dumpmultimap(name, table, w):
|
||||
print("")
|
||||
print("static const Rune " + name + "[] = {")
|
||||
for list in table:
|
||||
list += [0] * (w - len(list))
|
||||
print(",".join(map(hex, list)) + ",")
|
||||
print("};")
|
||||
|
||||
print("/* This file was automatically created from " + sys.argv[1] + " */")
|
||||
dumpalpha()
|
||||
dumpmap("ucd_tolower", tolower)
|
||||
dumpmap("ucd_toupper", toupper)
|
||||
dumpmultimap("ucd_tolower_full", tolower_full, 4)
|
||||
dumpmultimap("ucd_toupper_full", toupper_full, 5)
|
832
src/mujs/jsarray.c
Normal file
832
src/mujs/jsarray.c
Normal file
|
@ -0,0 +1,832 @@
|
|||
#include "jsi.h"
|
||||
|
||||
#ifndef JS_HEAPSORT
|
||||
#define JS_HEAPSORT 0
|
||||
#endif
|
||||
|
||||
int js_getlength(js_State *J, int idx)
|
||||
{
|
||||
int len;
|
||||
js_getproperty(J, idx, "length");
|
||||
len = js_tointeger(J, -1);
|
||||
js_pop(J, 1);
|
||||
return len;
|
||||
}
|
||||
|
||||
void js_setlength(js_State *J, int idx, int len)
|
||||
{
|
||||
js_pushnumber(J, len);
|
||||
js_setproperty(J, idx < 0 ? idx - 1 : idx, "length");
|
||||
}
|
||||
|
||||
static void jsB_new_Array(js_State *J)
|
||||
{
|
||||
int i, top = js_gettop(J);
|
||||
|
||||
js_newarray(J);
|
||||
|
||||
if (top == 2) {
|
||||
if (js_isnumber(J, 1)) {
|
||||
js_copy(J, 1);
|
||||
js_setproperty(J, -2, "length");
|
||||
} else {
|
||||
js_copy(J, 1);
|
||||
js_setindex(J, -2, 0);
|
||||
}
|
||||
} else {
|
||||
for (i = 1; i < top; ++i) {
|
||||
js_copy(J, i);
|
||||
js_setindex(J, -2, i - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Ap_concat(js_State *J)
|
||||
{
|
||||
int i, top = js_gettop(J);
|
||||
int n, k, len;
|
||||
|
||||
js_newarray(J);
|
||||
n = 0;
|
||||
|
||||
for (i = 0; i < top; ++i) {
|
||||
js_copy(J, i);
|
||||
if (js_isarray(J, -1)) {
|
||||
len = js_getlength(J, -1);
|
||||
for (k = 0; k < len; ++k)
|
||||
if (js_hasindex(J, -1, k))
|
||||
js_setindex(J, -3, n++);
|
||||
js_pop(J, 1);
|
||||
} else {
|
||||
js_setindex(J, -2, n++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Ap_join(js_State *J)
|
||||
{
|
||||
char * volatile out = NULL;
|
||||
const char * volatile r = NULL;
|
||||
const char *sep;
|
||||
int seplen;
|
||||
int k, n, len, rlen;
|
||||
|
||||
len = js_getlength(J, 0);
|
||||
|
||||
if (js_isdefined(J, 1)) {
|
||||
sep = js_tostring(J, 1);
|
||||
seplen = strlen(sep);
|
||||
} else {
|
||||
sep = ",";
|
||||
seplen = 1;
|
||||
}
|
||||
|
||||
if (len <= 0) {
|
||||
js_pushliteral(J, "");
|
||||
return;
|
||||
}
|
||||
|
||||
if (js_try(J)) {
|
||||
js_free(J, out);
|
||||
js_throw(J);
|
||||
}
|
||||
|
||||
n = 0;
|
||||
for (k = 0; k < len; ++k) {
|
||||
js_getindex(J, 0, k);
|
||||
if (js_iscoercible(J, -1)) {
|
||||
r = js_tostring(J, -1);
|
||||
rlen = strlen(r);
|
||||
} else {
|
||||
rlen = 0;
|
||||
}
|
||||
|
||||
if (k == 0) {
|
||||
out = js_malloc(J, rlen + 1);
|
||||
if (rlen > 0) {
|
||||
memcpy(out, r, rlen);
|
||||
n += rlen;
|
||||
}
|
||||
} else {
|
||||
if (n + seplen + rlen > JS_STRLIMIT)
|
||||
js_rangeerror(J, "invalid string length");
|
||||
out = js_realloc(J, out, n + seplen + rlen + 1);
|
||||
if (seplen > 0) {
|
||||
memcpy(out + n, sep, seplen);
|
||||
n += seplen;
|
||||
}
|
||||
if (rlen > 0) {
|
||||
memcpy(out + n, r, rlen);
|
||||
n += rlen;
|
||||
}
|
||||
}
|
||||
|
||||
js_pop(J, 1);
|
||||
}
|
||||
|
||||
js_pushlstring(J, out, n);
|
||||
js_endtry(J);
|
||||
js_free(J, out);
|
||||
}
|
||||
|
||||
static void Ap_pop(js_State *J)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = js_getlength(J, 0);
|
||||
|
||||
if (n > 0) {
|
||||
js_getindex(J, 0, n - 1);
|
||||
js_delindex(J, 0, n - 1);
|
||||
js_setlength(J, 0, n - 1);
|
||||
} else {
|
||||
js_setlength(J, 0, 0);
|
||||
js_pushundefined(J);
|
||||
}
|
||||
}
|
||||
|
||||
static void Ap_push(js_State *J)
|
||||
{
|
||||
int i, top = js_gettop(J);
|
||||
int n;
|
||||
|
||||
n = js_getlength(J, 0);
|
||||
|
||||
for (i = 1; i < top; ++i, ++n) {
|
||||
js_copy(J, i);
|
||||
js_setindex(J, 0, n);
|
||||
}
|
||||
|
||||
js_setlength(J, 0, n);
|
||||
|
||||
js_pushnumber(J, n);
|
||||
}
|
||||
|
||||
static void Ap_reverse(js_State *J)
|
||||
{
|
||||
int len, middle, lower;
|
||||
|
||||
len = js_getlength(J, 0);
|
||||
middle = len / 2;
|
||||
lower = 0;
|
||||
|
||||
while (lower != middle) {
|
||||
int upper = len - lower - 1;
|
||||
int haslower = js_hasindex(J, 0, lower);
|
||||
int hasupper = js_hasindex(J, 0, upper);
|
||||
if (haslower && hasupper) {
|
||||
js_setindex(J, 0, lower);
|
||||
js_setindex(J, 0, upper);
|
||||
} else if (hasupper) {
|
||||
js_setindex(J, 0, lower);
|
||||
js_delindex(J, 0, upper);
|
||||
} else if (haslower) {
|
||||
js_setindex(J, 0, upper);
|
||||
js_delindex(J, 0, lower);
|
||||
}
|
||||
++lower;
|
||||
}
|
||||
|
||||
js_copy(J, 0);
|
||||
}
|
||||
|
||||
static void Ap_shift(js_State *J)
|
||||
{
|
||||
int k, len;
|
||||
|
||||
len = js_getlength(J, 0);
|
||||
|
||||
if (len == 0) {
|
||||
js_setlength(J, 0, 0);
|
||||
js_pushundefined(J);
|
||||
return;
|
||||
}
|
||||
|
||||
js_getindex(J, 0, 0);
|
||||
|
||||
for (k = 1; k < len; ++k) {
|
||||
if (js_hasindex(J, 0, k))
|
||||
js_setindex(J, 0, k - 1);
|
||||
else
|
||||
js_delindex(J, 0, k - 1);
|
||||
}
|
||||
|
||||
js_delindex(J, 0, len - 1);
|
||||
js_setlength(J, 0, len - 1);
|
||||
}
|
||||
|
||||
static void Ap_slice(js_State *J)
|
||||
{
|
||||
int len, s, e, n;
|
||||
double sv, ev;
|
||||
|
||||
js_newarray(J);
|
||||
|
||||
len = js_getlength(J, 0);
|
||||
sv = js_tointeger(J, 1);
|
||||
ev = js_isdefined(J, 2) ? js_tointeger(J, 2) : len;
|
||||
|
||||
if (sv < 0) sv = sv + len;
|
||||
if (ev < 0) ev = ev + len;
|
||||
|
||||
s = sv < 0 ? 0 : sv > len ? len : sv;
|
||||
e = ev < 0 ? 0 : ev > len ? len : ev;
|
||||
|
||||
for (n = 0; s < e; ++s, ++n)
|
||||
if (js_hasindex(J, 0, s))
|
||||
js_setindex(J, -2, n);
|
||||
}
|
||||
|
||||
static int Ap_sort_cmp(js_State *J, int idx_a, int idx_b)
|
||||
{
|
||||
js_Object *obj = js_tovalue(J, 0)->u.object;
|
||||
if (obj->u.a.simple) {
|
||||
js_Value *val_a = &obj->u.a.array[idx_a];
|
||||
js_Value *val_b = &obj->u.a.array[idx_b];
|
||||
int und_a = val_a->t.type == JS_TUNDEFINED;
|
||||
int und_b = val_b->t.type == JS_TUNDEFINED;
|
||||
if (und_a) return und_b;
|
||||
if (und_b) return -1;
|
||||
if (js_iscallable(J, 1)) {
|
||||
double v;
|
||||
js_copy(J, 1); /* copy function */
|
||||
js_pushundefined(J); /* no 'this' binding */
|
||||
js_pushvalue(J, *val_a);
|
||||
js_pushvalue(J, *val_b);
|
||||
js_call(J, 2);
|
||||
v = js_tonumber(J, -1);
|
||||
js_pop(J, 1);
|
||||
if (isnan(v))
|
||||
return 0;
|
||||
if (v == 0)
|
||||
return 0;
|
||||
return v < 0 ? -1 : 1;
|
||||
} else {
|
||||
const char *str_a, *str_b;
|
||||
int c;
|
||||
js_pushvalue(J, *val_a);
|
||||
js_pushvalue(J, *val_b);
|
||||
str_a = js_tostring(J, -2);
|
||||
str_b = js_tostring(J, -1);
|
||||
c = strcmp(str_a, str_b);
|
||||
js_pop(J, 2);
|
||||
return c;
|
||||
}
|
||||
} else {
|
||||
int und_a, und_b;
|
||||
int has_a = js_hasindex(J, 0, idx_a);
|
||||
int has_b = js_hasindex(J, 0, idx_b);
|
||||
if (!has_a && !has_b) {
|
||||
return 0;
|
||||
}
|
||||
if (has_a && !has_b) {
|
||||
js_pop(J, 1);
|
||||
return -1;
|
||||
}
|
||||
if (!has_a && has_b) {
|
||||
js_pop(J, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
und_a = js_isundefined(J, -2);
|
||||
und_b = js_isundefined(J, -1);
|
||||
if (und_a) {
|
||||
js_pop(J, 2);
|
||||
return und_b;
|
||||
}
|
||||
if (und_b) {
|
||||
js_pop(J, 2);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (js_iscallable(J, 1)) {
|
||||
double v;
|
||||
js_copy(J, 1); /* copy function */
|
||||
js_pushundefined(J); /* no 'this' binding */
|
||||
js_copy(J, -4);
|
||||
js_copy(J, -4);
|
||||
js_call(J, 2);
|
||||
v = js_tonumber(J, -1);
|
||||
js_pop(J, 3);
|
||||
if (isnan(v))
|
||||
return 0;
|
||||
if (v == 0)
|
||||
return 0;
|
||||
return v < 0 ? -1 : 1;
|
||||
} else {
|
||||
const char *str_a = js_tostring(J, -2);
|
||||
const char *str_b = js_tostring(J, -1);
|
||||
int c = strcmp(str_a, str_b);
|
||||
js_pop(J, 2);
|
||||
return c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Ap_sort_swap(js_State *J, int idx_a, int idx_b)
|
||||
{
|
||||
js_Object *obj = js_tovalue(J, 0)->u.object;
|
||||
if (obj->u.a.simple) {
|
||||
js_Value tmp = obj->u.a.array[idx_a];
|
||||
obj->u.a.array[idx_a] = obj->u.a.array[idx_b];
|
||||
obj->u.a.array[idx_b] = tmp;
|
||||
} else {
|
||||
int has_a = js_hasindex(J, 0, idx_a);
|
||||
int has_b = js_hasindex(J, 0, idx_b);
|
||||
if (has_a && has_b) {
|
||||
js_setindex(J, 0, idx_a);
|
||||
js_setindex(J, 0, idx_b);
|
||||
} else if (has_a && !has_b) {
|
||||
js_delindex(J, 0, idx_a);
|
||||
js_setindex(J, 0, idx_b);
|
||||
} else if (!has_a && has_b) {
|
||||
js_delindex(J, 0, idx_b);
|
||||
js_setindex(J, 0, idx_a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* A bottom-up/bouncing heapsort implementation */
|
||||
|
||||
static int Ap_sort_leaf(js_State *J, int i, int end)
|
||||
{
|
||||
int j = i;
|
||||
int lc = (j << 1) + 1; /* left child */
|
||||
int rc = (j << 1) + 2; /* right child */
|
||||
while (rc < end) {
|
||||
if (Ap_sort_cmp(J, rc, lc) > 0)
|
||||
j = rc;
|
||||
else
|
||||
j = lc;
|
||||
lc = (j << 1) + 1;
|
||||
rc = (j << 1) + 2;
|
||||
}
|
||||
if (lc < end)
|
||||
j = lc;
|
||||
return j;
|
||||
}
|
||||
|
||||
static void Ap_sort_sift(js_State *J, int i, int end)
|
||||
{
|
||||
int j = Ap_sort_leaf(J, i, end);
|
||||
while (Ap_sort_cmp(J, i, j) > 0)
|
||||
j = (j - 1) >> 1; /* parent */
|
||||
while (j > i) {
|
||||
Ap_sort_swap(J, i, j);
|
||||
j = (j - 1) >> 1; /* parent */
|
||||
}
|
||||
}
|
||||
|
||||
static void Ap_sort_heapsort(js_State *J, int n)
|
||||
{
|
||||
int i;
|
||||
for (i = n / 2 - 1; i >= 0; --i)
|
||||
Ap_sort_sift(J, i, n);
|
||||
for (i = n - 1; i > 0; --i) {
|
||||
Ap_sort_swap(J, 0, i);
|
||||
Ap_sort_sift(J, 0, i);
|
||||
}
|
||||
}
|
||||
|
||||
static void Ap_sort(js_State *J)
|
||||
{
|
||||
int len;
|
||||
|
||||
len = js_getlength(J, 0);
|
||||
if (len <= 1) {
|
||||
js_copy(J, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!js_iscallable(J, 1) && !js_isundefined(J, 1))
|
||||
js_typeerror(J, "comparison function must be a function or undefined");
|
||||
|
||||
if (len >= INT_MAX)
|
||||
js_rangeerror(J, "array is too large to sort");
|
||||
|
||||
Ap_sort_heapsort(J, len);
|
||||
|
||||
js_copy(J, 0);
|
||||
}
|
||||
|
||||
static void Ap_splice(js_State *J)
|
||||
{
|
||||
int top = js_gettop(J);
|
||||
int len, start, del, add, k;
|
||||
|
||||
len = js_getlength(J, 0);
|
||||
start = js_tointeger(J, 1);
|
||||
if (start < 0)
|
||||
start = (len + start) > 0 ? len + start : 0;
|
||||
else if (start > len)
|
||||
start = len;
|
||||
|
||||
if (js_isdefined(J, 2))
|
||||
del = js_tointeger(J, 2);
|
||||
else
|
||||
del = len - start;
|
||||
if (del > len - start)
|
||||
del = len - start;
|
||||
if (del < 0)
|
||||
del = 0;
|
||||
|
||||
js_newarray(J);
|
||||
|
||||
/* copy deleted items to return array */
|
||||
for (k = 0; k < del; ++k)
|
||||
if (js_hasindex(J, 0, start + k))
|
||||
js_setindex(J, -2, k);
|
||||
js_setlength(J, -1, del);
|
||||
|
||||
/* shift the tail to resize the hole left by deleted items */
|
||||
add = top - 3;
|
||||
if (add < del) {
|
||||
for (k = start; k < len - del; ++k) {
|
||||
if (js_hasindex(J, 0, k + del))
|
||||
js_setindex(J, 0, k + add);
|
||||
else
|
||||
js_delindex(J, 0, k + add);
|
||||
}
|
||||
for (k = len; k > len - del + add; --k)
|
||||
js_delindex(J, 0, k - 1);
|
||||
} else if (add > del) {
|
||||
for (k = len - del; k > start; --k) {
|
||||
if (js_hasindex(J, 0, k + del - 1))
|
||||
js_setindex(J, 0, k + add - 1);
|
||||
else
|
||||
js_delindex(J, 0, k + add - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* copy new items into the hole */
|
||||
for (k = 0; k < add; ++k) {
|
||||
js_copy(J, 3 + k);
|
||||
js_setindex(J, 0, start + k);
|
||||
}
|
||||
|
||||
js_setlength(J, 0, len - del + add);
|
||||
}
|
||||
|
||||
static void Ap_unshift(js_State *J)
|
||||
{
|
||||
int i, top = js_gettop(J);
|
||||
int k, len;
|
||||
|
||||
len = js_getlength(J, 0);
|
||||
|
||||
for (k = len; k > 0; --k) {
|
||||
int from = k - 1;
|
||||
int to = k + top - 2;
|
||||
if (js_hasindex(J, 0, from))
|
||||
js_setindex(J, 0, to);
|
||||
else
|
||||
js_delindex(J, 0, to);
|
||||
}
|
||||
|
||||
for (i = 1; i < top; ++i) {
|
||||
js_copy(J, i);
|
||||
js_setindex(J, 0, i - 1);
|
||||
}
|
||||
|
||||
js_setlength(J, 0, len + top - 1);
|
||||
|
||||
js_pushnumber(J, len + top - 1);
|
||||
}
|
||||
|
||||
static void Ap_toString(js_State *J)
|
||||
{
|
||||
if (!js_iscoercible(J, 0))
|
||||
js_typeerror(J, "'this' is not an object");
|
||||
js_getproperty(J, 0, "join");
|
||||
if (!js_iscallable(J, -1)) {
|
||||
js_pop(J, 1);
|
||||
/* TODO: call Object.prototype.toString implementation directly */
|
||||
js_getglobal(J, "Object");
|
||||
js_getproperty(J, -1, "prototype");
|
||||
js_rot2pop1(J);
|
||||
js_getproperty(J, -1, "toString");
|
||||
js_rot2pop1(J);
|
||||
}
|
||||
js_copy(J, 0);
|
||||
js_call(J, 0);
|
||||
}
|
||||
|
||||
static void Ap_indexOf(js_State *J)
|
||||
{
|
||||
int k, len, from;
|
||||
|
||||
len = js_getlength(J, 0);
|
||||
from = js_isdefined(J, 2) ? js_tointeger(J, 2) : 0;
|
||||
if (from < 0) from = len + from;
|
||||
if (from < 0) from = 0;
|
||||
|
||||
js_copy(J, 1);
|
||||
for (k = from; k < len; ++k) {
|
||||
if (js_hasindex(J, 0, k)) {
|
||||
if (js_strictequal(J)) {
|
||||
js_pushnumber(J, k);
|
||||
return;
|
||||
}
|
||||
js_pop(J, 1);
|
||||
}
|
||||
}
|
||||
|
||||
js_pushnumber(J, -1);
|
||||
}
|
||||
|
||||
static void Ap_lastIndexOf(js_State *J)
|
||||
{
|
||||
int k, len, from;
|
||||
|
||||
len = js_getlength(J, 0);
|
||||
from = js_isdefined(J, 2) ? js_tointeger(J, 2) : len - 1;
|
||||
if (from > len - 1) from = len - 1;
|
||||
if (from < 0) from = len + from;
|
||||
|
||||
js_copy(J, 1);
|
||||
for (k = from; k >= 0; --k) {
|
||||
if (js_hasindex(J, 0, k)) {
|
||||
if (js_strictequal(J)) {
|
||||
js_pushnumber(J, k);
|
||||
return;
|
||||
}
|
||||
js_pop(J, 1);
|
||||
}
|
||||
}
|
||||
|
||||
js_pushnumber(J, -1);
|
||||
}
|
||||
|
||||
static void Ap_every(js_State *J)
|
||||
{
|
||||
int hasthis = js_gettop(J) >= 3;
|
||||
int k, len;
|
||||
|
||||
if (!js_iscallable(J, 1))
|
||||
js_typeerror(J, "callback is not a function");
|
||||
|
||||
len = js_getlength(J, 0);
|
||||
for (k = 0; k < len; ++k) {
|
||||
if (js_hasindex(J, 0, k)) {
|
||||
js_copy(J, 1);
|
||||
if (hasthis)
|
||||
js_copy(J, 2);
|
||||
else
|
||||
js_pushundefined(J);
|
||||
js_copy(J, -3);
|
||||
js_pushnumber(J, k);
|
||||
js_copy(J, 0);
|
||||
js_call(J, 3);
|
||||
if (!js_toboolean(J, -1))
|
||||
return;
|
||||
js_pop(J, 2);
|
||||
}
|
||||
}
|
||||
|
||||
js_pushboolean(J, 1);
|
||||
}
|
||||
|
||||
static void Ap_some(js_State *J)
|
||||
{
|
||||
int hasthis = js_gettop(J) >= 3;
|
||||
int k, len;
|
||||
|
||||
if (!js_iscallable(J, 1))
|
||||
js_typeerror(J, "callback is not a function");
|
||||
|
||||
len = js_getlength(J, 0);
|
||||
for (k = 0; k < len; ++k) {
|
||||
if (js_hasindex(J, 0, k)) {
|
||||
js_copy(J, 1);
|
||||
if (hasthis)
|
||||
js_copy(J, 2);
|
||||
else
|
||||
js_pushundefined(J);
|
||||
js_copy(J, -3);
|
||||
js_pushnumber(J, k);
|
||||
js_copy(J, 0);
|
||||
js_call(J, 3);
|
||||
if (js_toboolean(J, -1))
|
||||
return;
|
||||
js_pop(J, 2);
|
||||
}
|
||||
}
|
||||
|
||||
js_pushboolean(J, 0);
|
||||
}
|
||||
|
||||
static void Ap_forEach(js_State *J)
|
||||
{
|
||||
int hasthis = js_gettop(J) >= 3;
|
||||
int k, len;
|
||||
|
||||
if (!js_iscallable(J, 1))
|
||||
js_typeerror(J, "callback is not a function");
|
||||
|
||||
len = js_getlength(J, 0);
|
||||
for (k = 0; k < len; ++k) {
|
||||
if (js_hasindex(J, 0, k)) {
|
||||
js_copy(J, 1);
|
||||
if (hasthis)
|
||||
js_copy(J, 2);
|
||||
else
|
||||
js_pushundefined(J);
|
||||
js_copy(J, -3);
|
||||
js_pushnumber(J, k);
|
||||
js_copy(J, 0);
|
||||
js_call(J, 3);
|
||||
js_pop(J, 2);
|
||||
}
|
||||
}
|
||||
|
||||
js_pushundefined(J);
|
||||
}
|
||||
|
||||
static void Ap_map(js_State *J)
|
||||
{
|
||||
int hasthis = js_gettop(J) >= 3;
|
||||
int k, len;
|
||||
|
||||
if (!js_iscallable(J, 1))
|
||||
js_typeerror(J, "callback is not a function");
|
||||
|
||||
js_newarray(J);
|
||||
|
||||
len = js_getlength(J, 0);
|
||||
for (k = 0; k < len; ++k) {
|
||||
if (js_hasindex(J, 0, k)) {
|
||||
js_copy(J, 1);
|
||||
if (hasthis)
|
||||
js_copy(J, 2);
|
||||
else
|
||||
js_pushundefined(J);
|
||||
js_copy(J, -3);
|
||||
js_pushnumber(J, k);
|
||||
js_copy(J, 0);
|
||||
js_call(J, 3);
|
||||
js_setindex(J, -3, k);
|
||||
js_pop(J, 1);
|
||||
}
|
||||
}
|
||||
js_setlength(J, -1, len);
|
||||
}
|
||||
|
||||
static void Ap_filter(js_State *J)
|
||||
{
|
||||
int hasthis = js_gettop(J) >= 3;
|
||||
int k, to, len;
|
||||
|
||||
if (!js_iscallable(J, 1))
|
||||
js_typeerror(J, "callback is not a function");
|
||||
|
||||
js_newarray(J);
|
||||
to = 0;
|
||||
|
||||
len = js_getlength(J, 0);
|
||||
for (k = 0; k < len; ++k) {
|
||||
if (js_hasindex(J, 0, k)) {
|
||||
js_copy(J, 1);
|
||||
if (hasthis)
|
||||
js_copy(J, 2);
|
||||
else
|
||||
js_pushundefined(J);
|
||||
js_copy(J, -3);
|
||||
js_pushnumber(J, k);
|
||||
js_copy(J, 0);
|
||||
js_call(J, 3);
|
||||
if (js_toboolean(J, -1)) {
|
||||
js_pop(J, 1);
|
||||
js_setindex(J, -2, to++);
|
||||
} else {
|
||||
js_pop(J, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Ap_reduce(js_State *J)
|
||||
{
|
||||
int hasinitial = js_gettop(J) >= 3;
|
||||
int k, len;
|
||||
|
||||
if (!js_iscallable(J, 1))
|
||||
js_typeerror(J, "callback is not a function");
|
||||
|
||||
len = js_getlength(J, 0);
|
||||
k = 0;
|
||||
|
||||
if (len == 0 && !hasinitial)
|
||||
js_typeerror(J, "no initial value");
|
||||
|
||||
/* initial value of accumulator */
|
||||
if (hasinitial)
|
||||
js_copy(J, 2);
|
||||
else {
|
||||
while (k < len)
|
||||
if (js_hasindex(J, 0, k++))
|
||||
break;
|
||||
if (k == len)
|
||||
js_typeerror(J, "no initial value");
|
||||
}
|
||||
|
||||
while (k < len) {
|
||||
if (js_hasindex(J, 0, k)) {
|
||||
js_copy(J, 1);
|
||||
js_pushundefined(J);
|
||||
js_rot(J, 4); /* accumulator on top */
|
||||
js_rot(J, 4); /* property on top */
|
||||
js_pushnumber(J, k);
|
||||
js_copy(J, 0);
|
||||
js_call(J, 4); /* calculate new accumulator */
|
||||
}
|
||||
++k;
|
||||
}
|
||||
|
||||
/* return accumulator */
|
||||
}
|
||||
|
||||
static void Ap_reduceRight(js_State *J)
|
||||
{
|
||||
int hasinitial = js_gettop(J) >= 3;
|
||||
int k, len;
|
||||
|
||||
if (!js_iscallable(J, 1))
|
||||
js_typeerror(J, "callback is not a function");
|
||||
|
||||
len = js_getlength(J, 0);
|
||||
k = len - 1;
|
||||
|
||||
if (len == 0 && !hasinitial)
|
||||
js_typeerror(J, "no initial value");
|
||||
|
||||
/* initial value of accumulator */
|
||||
if (hasinitial)
|
||||
js_copy(J, 2);
|
||||
else {
|
||||
while (k >= 0)
|
||||
if (js_hasindex(J, 0, k--))
|
||||
break;
|
||||
if (k < 0)
|
||||
js_typeerror(J, "no initial value");
|
||||
}
|
||||
|
||||
while (k >= 0) {
|
||||
if (js_hasindex(J, 0, k)) {
|
||||
js_copy(J, 1);
|
||||
js_pushundefined(J);
|
||||
js_rot(J, 4); /* accumulator on top */
|
||||
js_rot(J, 4); /* property on top */
|
||||
js_pushnumber(J, k);
|
||||
js_copy(J, 0);
|
||||
js_call(J, 4); /* calculate new accumulator */
|
||||
}
|
||||
--k;
|
||||
}
|
||||
|
||||
/* return accumulator */
|
||||
}
|
||||
|
||||
static void A_isArray(js_State *J)
|
||||
{
|
||||
if (js_isobject(J, 1)) {
|
||||
js_Object *T = js_toobject(J, 1);
|
||||
js_pushboolean(J, T->type == JS_CARRAY);
|
||||
} else {
|
||||
js_pushboolean(J, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void jsB_initarray(js_State *J)
|
||||
{
|
||||
js_pushobject(J, J->Array_prototype);
|
||||
{
|
||||
jsB_propf(J, "Array.prototype.toString", Ap_toString, 0);
|
||||
jsB_propf(J, "Array.prototype.concat", Ap_concat, 0); /* 1 */
|
||||
jsB_propf(J, "Array.prototype.join", Ap_join, 1);
|
||||
jsB_propf(J, "Array.prototype.pop", Ap_pop, 0);
|
||||
jsB_propf(J, "Array.prototype.push", Ap_push, 0); /* 1 */
|
||||
jsB_propf(J, "Array.prototype.reverse", Ap_reverse, 0);
|
||||
jsB_propf(J, "Array.prototype.shift", Ap_shift, 0);
|
||||
jsB_propf(J, "Array.prototype.slice", Ap_slice, 2);
|
||||
jsB_propf(J, "Array.prototype.sort", Ap_sort, 1);
|
||||
jsB_propf(J, "Array.prototype.splice", Ap_splice, 2);
|
||||
jsB_propf(J, "Array.prototype.unshift", Ap_unshift, 0); /* 1 */
|
||||
|
||||
/* ES5 */
|
||||
jsB_propf(J, "Array.prototype.indexOf", Ap_indexOf, 1);
|
||||
jsB_propf(J, "Array.prototype.lastIndexOf", Ap_lastIndexOf, 1);
|
||||
jsB_propf(J, "Array.prototype.every", Ap_every, 1);
|
||||
jsB_propf(J, "Array.prototype.some", Ap_some, 1);
|
||||
jsB_propf(J, "Array.prototype.forEach", Ap_forEach, 1);
|
||||
jsB_propf(J, "Array.prototype.map", Ap_map, 1);
|
||||
jsB_propf(J, "Array.prototype.filter", Ap_filter, 1);
|
||||
jsB_propf(J, "Array.prototype.reduce", Ap_reduce, 1);
|
||||
jsB_propf(J, "Array.prototype.reduceRight", Ap_reduceRight, 1);
|
||||
}
|
||||
js_newcconstructor(J, jsB_new_Array, jsB_new_Array, "Array", 0); /* 1 */
|
||||
{
|
||||
/* ES5 */
|
||||
jsB_propf(J, "Array.isArray", A_isArray, 1);
|
||||
}
|
||||
js_defglobal(J, "Array", JS_DONTENUM);
|
||||
}
|
38
src/mujs/jsboolean.c
Normal file
38
src/mujs/jsboolean.c
Normal file
|
@ -0,0 +1,38 @@
|
|||
#include "jsi.h"
|
||||
|
||||
static void jsB_new_Boolean(js_State *J)
|
||||
{
|
||||
js_newboolean(J, js_toboolean(J, 1));
|
||||
}
|
||||
|
||||
static void jsB_Boolean(js_State *J)
|
||||
{
|
||||
js_pushboolean(J, js_toboolean(J, 1));
|
||||
}
|
||||
|
||||
static void Bp_toString(js_State *J)
|
||||
{
|
||||
js_Object *self = js_toobject(J, 0);
|
||||
if (self->type != JS_CBOOLEAN) js_typeerror(J, "not a boolean");
|
||||
js_pushliteral(J, self->u.boolean ? "true" : "false");
|
||||
}
|
||||
|
||||
static void Bp_valueOf(js_State *J)
|
||||
{
|
||||
js_Object *self = js_toobject(J, 0);
|
||||
if (self->type != JS_CBOOLEAN) js_typeerror(J, "not a boolean");
|
||||
js_pushboolean(J, self->u.boolean);
|
||||
}
|
||||
|
||||
void jsB_initboolean(js_State *J)
|
||||
{
|
||||
J->Boolean_prototype->u.boolean = 0;
|
||||
|
||||
js_pushobject(J, J->Boolean_prototype);
|
||||
{
|
||||
jsB_propf(J, "Boolean.prototype.toString", Bp_toString, 0);
|
||||
jsB_propf(J, "Boolean.prototype.valueOf", Bp_valueOf, 0);
|
||||
}
|
||||
js_newcconstructor(J, jsB_Boolean, jsB_new_Boolean, "Boolean", 1);
|
||||
js_defglobal(J, "Boolean", JS_DONTENUM);
|
||||
}
|
249
src/mujs/jsbuiltin.c
Normal file
249
src/mujs/jsbuiltin.c
Normal file
|
@ -0,0 +1,249 @@
|
|||
#include "jsi.h"
|
||||
#include "regexp.h"
|
||||
|
||||
static void jsB_globalf(js_State *J, const char *name, js_CFunction cfun, int n)
|
||||
{
|
||||
js_newcfunction(J, cfun, name, n);
|
||||
js_defglobal(J, name, JS_DONTENUM);
|
||||
}
|
||||
|
||||
void jsB_propf(js_State *J, const char *name, js_CFunction cfun, int n)
|
||||
{
|
||||
const char *pname = strrchr(name, '.');
|
||||
pname = pname ? pname + 1 : name;
|
||||
js_newcfunction(J, cfun, name, n);
|
||||
js_defproperty(J, -2, pname, JS_DONTENUM);
|
||||
}
|
||||
|
||||
void jsB_propn(js_State *J, const char *name, double number)
|
||||
{
|
||||
js_pushnumber(J, number);
|
||||
js_defproperty(J, -2, name, JS_READONLY | JS_DONTENUM | JS_DONTCONF);
|
||||
}
|
||||
|
||||
void jsB_props(js_State *J, const char *name, const char *string)
|
||||
{
|
||||
js_pushliteral(J, string);
|
||||
js_defproperty(J, -2, name, JS_DONTENUM);
|
||||
}
|
||||
|
||||
static void jsB_parseInt(js_State *J)
|
||||
{
|
||||
const char *s = js_tostring(J, 1);
|
||||
int radix = js_isdefined(J, 2) ? js_tointeger(J, 2) : 0;
|
||||
double sign = 1;
|
||||
double n;
|
||||
char *e;
|
||||
|
||||
while (jsY_iswhite(*s) || jsY_isnewline(*s))
|
||||
++s;
|
||||
if (*s == '-') {
|
||||
++s;
|
||||
sign = -1;
|
||||
} else if (*s == '+') {
|
||||
++s;
|
||||
}
|
||||
if (radix == 0) {
|
||||
radix = 10;
|
||||
if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
|
||||
s += 2;
|
||||
radix = 16;
|
||||
}
|
||||
} else if (radix < 2 || radix > 36) {
|
||||
js_pushnumber(J, NAN);
|
||||
return;
|
||||
}
|
||||
n = js_strtol(s, &e, radix);
|
||||
if (s == e)
|
||||
js_pushnumber(J, NAN);
|
||||
else
|
||||
js_pushnumber(J, n * sign);
|
||||
}
|
||||
|
||||
static void jsB_parseFloat(js_State *J)
|
||||
{
|
||||
const char *s = js_tostring(J, 1);
|
||||
char *e;
|
||||
double n;
|
||||
|
||||
while (jsY_iswhite(*s) || jsY_isnewline(*s)) ++s;
|
||||
if (!strncmp(s, "Infinity", 8))
|
||||
js_pushnumber(J, INFINITY);
|
||||
else if (!strncmp(s, "+Infinity", 9))
|
||||
js_pushnumber(J, INFINITY);
|
||||
else if (!strncmp(s, "-Infinity", 9))
|
||||
js_pushnumber(J, -INFINITY);
|
||||
else {
|
||||
n = js_stringtofloat(s, &e);
|
||||
if (e == s)
|
||||
js_pushnumber(J, NAN);
|
||||
else
|
||||
js_pushnumber(J, n);
|
||||
}
|
||||
}
|
||||
|
||||
static void jsB_isNaN(js_State *J)
|
||||
{
|
||||
double n = js_tonumber(J, 1);
|
||||
js_pushboolean(J, isnan(n));
|
||||
}
|
||||
|
||||
static void jsB_isFinite(js_State *J)
|
||||
{
|
||||
double n = js_tonumber(J, 1);
|
||||
js_pushboolean(J, isfinite(n));
|
||||
}
|
||||
|
||||
static void Encode(js_State *J, const char *str_, const char *unescaped)
|
||||
{
|
||||
/* NOTE: volatile to silence GCC warning about longjmp clobbering a variable */
|
||||
const char * volatile str = str_;
|
||||
js_Buffer *sb = NULL;
|
||||
|
||||
static const char *HEX = "0123456789ABCDEF";
|
||||
|
||||
if (js_try(J)) {
|
||||
js_free(J, sb);
|
||||
js_throw(J);
|
||||
}
|
||||
|
||||
while (*str) {
|
||||
int c = (unsigned char) *str++;
|
||||
if (strchr(unescaped, c))
|
||||
js_putc(J, &sb, c);
|
||||
else {
|
||||
js_putc(J, &sb, '%');
|
||||
js_putc(J, &sb, HEX[(c >> 4) & 0xf]);
|
||||
js_putc(J, &sb, HEX[c & 0xf]);
|
||||
}
|
||||
}
|
||||
js_putc(J, &sb, 0);
|
||||
|
||||
js_pushstring(J, sb ? sb->s : "");
|
||||
js_endtry(J);
|
||||
js_free(J, sb);
|
||||
}
|
||||
|
||||
static void Decode(js_State *J, const char *str_, const char *reserved)
|
||||
{
|
||||
/* NOTE: volatile to silence GCC warning about longjmp clobbering a variable */
|
||||
const char * volatile str = str_;
|
||||
js_Buffer *sb = NULL;
|
||||
int a, b;
|
||||
|
||||
if (js_try(J)) {
|
||||
js_free(J, sb);
|
||||
js_throw(J);
|
||||
}
|
||||
|
||||
while (*str) {
|
||||
int c = (unsigned char) *str++;
|
||||
if (c != '%')
|
||||
js_putc(J, &sb, c);
|
||||
else {
|
||||
if (!str[0] || !str[1])
|
||||
js_urierror(J, "truncated escape sequence");
|
||||
a = *str++;
|
||||
b = *str++;
|
||||
if (!jsY_ishex(a) || !jsY_ishex(b))
|
||||
js_urierror(J, "invalid escape sequence");
|
||||
c = jsY_tohex(a) << 4 | jsY_tohex(b);
|
||||
if (!strchr(reserved, c))
|
||||
js_putc(J, &sb, c);
|
||||
else {
|
||||
js_putc(J, &sb, '%');
|
||||
js_putc(J, &sb, a);
|
||||
js_putc(J, &sb, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
js_putc(J, &sb, 0);
|
||||
|
||||
js_pushstring(J, sb ? sb->s : "");
|
||||
js_endtry(J);
|
||||
js_free(J, sb);
|
||||
}
|
||||
|
||||
#define URIRESERVED ";/?:@&=+$,"
|
||||
#define URIALPHA "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
#define URIDIGIT "0123456789"
|
||||
#define URIMARK "-_.!~*'()"
|
||||
#define URIUNESCAPED URIALPHA URIDIGIT URIMARK
|
||||
|
||||
static void jsB_decodeURI(js_State *J)
|
||||
{
|
||||
Decode(J, js_tostring(J, 1), URIRESERVED "#");
|
||||
}
|
||||
|
||||
static void jsB_decodeURIComponent(js_State *J)
|
||||
{
|
||||
Decode(J, js_tostring(J, 1), "");
|
||||
}
|
||||
|
||||
static void jsB_encodeURI(js_State *J)
|
||||
{
|
||||
Encode(J, js_tostring(J, 1), URIUNESCAPED URIRESERVED "#");
|
||||
}
|
||||
|
||||
static void jsB_encodeURIComponent(js_State *J)
|
||||
{
|
||||
Encode(J, js_tostring(J, 1), URIUNESCAPED);
|
||||
}
|
||||
|
||||
void jsB_init(js_State *J)
|
||||
{
|
||||
/* Create the prototype objects here, before the constructors */
|
||||
J->Object_prototype = jsV_newobject(J, JS_COBJECT, NULL);
|
||||
J->Array_prototype = jsV_newobject(J, JS_CARRAY, J->Object_prototype);
|
||||
J->Function_prototype = jsV_newobject(J, JS_CCFUNCTION, J->Object_prototype);
|
||||
J->Boolean_prototype = jsV_newobject(J, JS_CBOOLEAN, J->Object_prototype);
|
||||
J->Number_prototype = jsV_newobject(J, JS_CNUMBER, J->Object_prototype);
|
||||
J->String_prototype = jsV_newobject(J, JS_CSTRING, J->Object_prototype);
|
||||
J->Date_prototype = jsV_newobject(J, JS_CDATE, J->Object_prototype);
|
||||
|
||||
J->RegExp_prototype = jsV_newobject(J, JS_CREGEXP, J->Object_prototype);
|
||||
J->RegExp_prototype->u.r.prog = js_regcompx(J->alloc, J->actx, "(?:)", 0, NULL);
|
||||
J->RegExp_prototype->u.r.source = js_strdup(J, "(?:)");
|
||||
|
||||
/* All the native error types */
|
||||
J->Error_prototype = jsV_newobject(J, JS_CERROR, J->Object_prototype);
|
||||
J->EvalError_prototype = jsV_newobject(J, JS_CERROR, J->Error_prototype);
|
||||
J->RangeError_prototype = jsV_newobject(J, JS_CERROR, J->Error_prototype);
|
||||
J->ReferenceError_prototype = jsV_newobject(J, JS_CERROR, J->Error_prototype);
|
||||
J->SyntaxError_prototype = jsV_newobject(J, JS_CERROR, J->Error_prototype);
|
||||
J->TypeError_prototype = jsV_newobject(J, JS_CERROR, J->Error_prototype);
|
||||
J->URIError_prototype = jsV_newobject(J, JS_CERROR, J->Error_prototype);
|
||||
|
||||
/* Create the constructors and fill out the prototype objects */
|
||||
jsB_initobject(J);
|
||||
jsB_initarray(J);
|
||||
jsB_initfunction(J);
|
||||
jsB_initboolean(J);
|
||||
jsB_initnumber(J);
|
||||
jsB_initstring(J);
|
||||
jsB_initregexp(J);
|
||||
jsB_initdate(J);
|
||||
jsB_initerror(J);
|
||||
jsB_initmath(J);
|
||||
jsB_initjson(J);
|
||||
|
||||
/* Initialize the global object */
|
||||
js_pushnumber(J, NAN);
|
||||
js_defglobal(J, "NaN", JS_READONLY | JS_DONTENUM | JS_DONTCONF);
|
||||
|
||||
js_pushnumber(J, INFINITY);
|
||||
js_defglobal(J, "Infinity", JS_READONLY | JS_DONTENUM | JS_DONTCONF);
|
||||
|
||||
js_pushundefined(J);
|
||||
js_defglobal(J, "undefined", JS_READONLY | JS_DONTENUM | JS_DONTCONF);
|
||||
|
||||
jsB_globalf(J, "parseInt", jsB_parseInt, 1);
|
||||
jsB_globalf(J, "parseFloat", jsB_parseFloat, 1);
|
||||
jsB_globalf(J, "isNaN", jsB_isNaN, 1);
|
||||
jsB_globalf(J, "isFinite", jsB_isFinite, 1);
|
||||
|
||||
jsB_globalf(J, "decodeURI", jsB_decodeURI, 1);
|
||||
jsB_globalf(J, "decodeURIComponent", jsB_decodeURIComponent, 1);
|
||||
jsB_globalf(J, "encodeURI", jsB_encodeURI, 1);
|
||||
jsB_globalf(J, "encodeURIComponent", jsB_encodeURIComponent, 1);
|
||||
}
|
1431
src/mujs/jscompile.c
Normal file
1431
src/mujs/jscompile.c
Normal file
File diff suppressed because it is too large
Load diff
861
src/mujs/jsdate.c
Normal file
861
src/mujs/jsdate.c
Normal file
|
@ -0,0 +1,861 @@
|
|||
#include "jsi.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#if defined(__unix__) || defined(__APPLE__)
|
||||
#include <sys/time.h>
|
||||
#elif defined(_WIN32)
|
||||
#include <sys/timeb.h>
|
||||
#endif
|
||||
|
||||
#define js_optnumber(J,I,V) (js_isdefined(J,I) ? js_tonumber(J,I) : V)
|
||||
|
||||
static double Now(void)
|
||||
{
|
||||
#if defined(__unix__) || defined(__APPLE__)
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
return floor(tv.tv_sec * 1000.0 + tv.tv_usec / 1000.0);
|
||||
#elif defined(_WIN32)
|
||||
struct _timeb tv;
|
||||
_ftime(&tv);
|
||||
return tv.time * 1000.0 + tv.millitm;
|
||||
#else
|
||||
return time(NULL) * 1000.0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static double LocalTZA(void)
|
||||
{
|
||||
static int once = 1;
|
||||
static double tza = 0;
|
||||
if (once) {
|
||||
time_t now = time(NULL);
|
||||
time_t utc = mktime(gmtime(&now));
|
||||
time_t loc = mktime(localtime(&now));
|
||||
tza = (loc - utc) * 1000;
|
||||
once = 0;
|
||||
}
|
||||
return tza;
|
||||
}
|
||||
|
||||
static double DaylightSavingTA(double t)
|
||||
{
|
||||
return 0; /* TODO */
|
||||
}
|
||||
|
||||
/* Helpers from the ECMA 262 specification */
|
||||
|
||||
#define HoursPerDay 24.0
|
||||
#define MinutesPerDay (HoursPerDay * MinutesPerHour)
|
||||
#define MinutesPerHour 60.0
|
||||
#define SecondsPerDay (MinutesPerDay * SecondsPerMinute)
|
||||
#define SecondsPerHour (MinutesPerHour * SecondsPerMinute)
|
||||
#define SecondsPerMinute 60.0
|
||||
|
||||
#define msPerDay (SecondsPerDay * msPerSecond)
|
||||
#define msPerHour (SecondsPerHour * msPerSecond)
|
||||
#define msPerMinute (SecondsPerMinute * msPerSecond)
|
||||
#define msPerSecond 1000.0
|
||||
|
||||
static double pmod(double x, double y)
|
||||
{
|
||||
x = fmod(x, y);
|
||||
if (x < 0)
|
||||
x += y;
|
||||
return x;
|
||||
}
|
||||
|
||||
static int Day(double t)
|
||||
{
|
||||
return floor(t / msPerDay);
|
||||
}
|
||||
|
||||
static double TimeWithinDay(double t)
|
||||
{
|
||||
return pmod(t, msPerDay);
|
||||
}
|
||||
|
||||
static int DaysInYear(int y)
|
||||
{
|
||||
return y % 4 == 0 && (y % 100 || (y % 400 == 0)) ? 366 : 365;
|
||||
}
|
||||
|
||||
static int DayFromYear(int y)
|
||||
{
|
||||
return 365 * (y - 1970) +
|
||||
floor((y - 1969) / 4.0) -
|
||||
floor((y - 1901) / 100.0) +
|
||||
floor((y - 1601) / 400.0);
|
||||
}
|
||||
|
||||
static double TimeFromYear(int y)
|
||||
{
|
||||
return DayFromYear(y) * msPerDay;
|
||||
}
|
||||
|
||||
static int YearFromTime(double t)
|
||||
{
|
||||
int y = floor(t / (msPerDay * 365.2425)) + 1970;
|
||||
double t2 = TimeFromYear(y);
|
||||
if (t2 > t)
|
||||
--y;
|
||||
else if (t2 + msPerDay * DaysInYear(y) <= t)
|
||||
++y;
|
||||
return y;
|
||||
}
|
||||
|
||||
static int InLeapYear(double t)
|
||||
{
|
||||
return DaysInYear(YearFromTime(t)) == 366;
|
||||
}
|
||||
|
||||
static int DayWithinYear(double t)
|
||||
{
|
||||
return Day(t) - DayFromYear(YearFromTime(t));
|
||||
}
|
||||
|
||||
static int MonthFromTime(double t)
|
||||
{
|
||||
int day = DayWithinYear(t);
|
||||
int leap = InLeapYear(t);
|
||||
if (day < 31) return 0;
|
||||
if (day < 59 + leap) return 1;
|
||||
if (day < 90 + leap) return 2;
|
||||
if (day < 120 + leap) return 3;
|
||||
if (day < 151 + leap) return 4;
|
||||
if (day < 181 + leap) return 5;
|
||||
if (day < 212 + leap) return 6;
|
||||
if (day < 243 + leap) return 7;
|
||||
if (day < 273 + leap) return 8;
|
||||
if (day < 304 + leap) return 9;
|
||||
if (day < 334 + leap) return 10;
|
||||
return 11;
|
||||
}
|
||||
|
||||
static int DateFromTime(double t)
|
||||
{
|
||||
int day = DayWithinYear(t);
|
||||
int leap = InLeapYear(t);
|
||||
switch (MonthFromTime(t)) {
|
||||
case 0: return day + 1;
|
||||
case 1: return day - 30;
|
||||
case 2: return day - 58 - leap;
|
||||
case 3: return day - 89 - leap;
|
||||
case 4: return day - 119 - leap;
|
||||
case 5: return day - 150 - leap;
|
||||
case 6: return day - 180 - leap;
|
||||
case 7: return day - 211 - leap;
|
||||
case 8: return day - 242 - leap;
|
||||
case 9: return day - 272 - leap;
|
||||
case 10: return day - 303 - leap;
|
||||
default : return day - 333 - leap;
|
||||
}
|
||||
}
|
||||
|
||||
static int WeekDay(double t)
|
||||
{
|
||||
return pmod(Day(t) + 4, 7);
|
||||
}
|
||||
|
||||
static double LocalTime(double utc)
|
||||
{
|
||||
return utc + LocalTZA() + DaylightSavingTA(utc);
|
||||
}
|
||||
|
||||
static double UTC(double loc)
|
||||
{
|
||||
return loc - LocalTZA() - DaylightSavingTA(loc - LocalTZA());
|
||||
}
|
||||
|
||||
static int HourFromTime(double t)
|
||||
{
|
||||
return pmod(floor(t / msPerHour), HoursPerDay);
|
||||
}
|
||||
|
||||
static int MinFromTime(double t)
|
||||
{
|
||||
return pmod(floor(t / msPerMinute), MinutesPerHour);
|
||||
}
|
||||
|
||||
static int SecFromTime(double t)
|
||||
{
|
||||
return pmod(floor(t / msPerSecond), SecondsPerMinute);
|
||||
}
|
||||
|
||||
static int msFromTime(double t)
|
||||
{
|
||||
return pmod(t, msPerSecond);
|
||||
}
|
||||
|
||||
static double MakeTime(double hour, double min, double sec, double ms)
|
||||
{
|
||||
return ((hour * MinutesPerHour + min) * SecondsPerMinute + sec) * msPerSecond + ms;
|
||||
}
|
||||
|
||||
static double MakeDay(double y, double m, double date)
|
||||
{
|
||||
/*
|
||||
* The following array contains the day of year for the first day of
|
||||
* each month, where index 0 is January, and day 0 is January 1.
|
||||
*/
|
||||
static const double firstDayOfMonth[2][12] = {
|
||||
{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
|
||||
{0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}
|
||||
};
|
||||
|
||||
double yd, md;
|
||||
int im;
|
||||
|
||||
y += floor(m / 12);
|
||||
m = pmod(m, 12);
|
||||
|
||||
im = (int)m;
|
||||
if (im < 0 || im >= 12)
|
||||
return NAN;
|
||||
|
||||
yd = floor(TimeFromYear(y) / msPerDay);
|
||||
md = firstDayOfMonth[DaysInYear(y) == 366][im];
|
||||
|
||||
return yd + md + date - 1;
|
||||
}
|
||||
|
||||
static double MakeDate(double day, double time)
|
||||
{
|
||||
return day * msPerDay + time;
|
||||
}
|
||||
|
||||
static double TimeClip(double t)
|
||||
{
|
||||
if (!isfinite(t))
|
||||
return NAN;
|
||||
if (fabs(t) > 8.64e15)
|
||||
return NAN;
|
||||
return t < 0 ? -floor(-t) : floor(t);
|
||||
}
|
||||
|
||||
static int toint(const char **sp, int w, int *v)
|
||||
{
|
||||
const char *s = *sp;
|
||||
*v = 0;
|
||||
while (w--) {
|
||||
if (*s < '0' || *s > '9')
|
||||
return 0;
|
||||
*v = *v * 10 + (*s++ - '0');
|
||||
}
|
||||
*sp = s;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static double parseDateTime(const char *s)
|
||||
{
|
||||
int y = 1970, m = 1, d = 1, H = 0, M = 0, S = 0, ms = 0;
|
||||
int tza = 0;
|
||||
double t;
|
||||
|
||||
/* Parse ISO 8601 formatted date and time: */
|
||||
/* YYYY("-"MM("-"DD)?)?("T"HH":"mm(":"ss("."sss)?)?("Z"|[+-]HH(":"mm)?)?)? */
|
||||
|
||||
if (!toint(&s, 4, &y)) return NAN;
|
||||
if (*s == '-') {
|
||||
s += 1;
|
||||
if (!toint(&s, 2, &m)) return NAN;
|
||||
if (*s == '-') {
|
||||
s += 1;
|
||||
if (!toint(&s, 2, &d)) return NAN;
|
||||
}
|
||||
}
|
||||
|
||||
if (*s == 'T') {
|
||||
s += 1;
|
||||
if (!toint(&s, 2, &H)) return NAN;
|
||||
if (*s != ':') return NAN;
|
||||
s += 1;
|
||||
if (!toint(&s, 2, &M)) return NAN;
|
||||
if (*s == ':') {
|
||||
s += 1;
|
||||
if (!toint(&s, 2, &S)) return NAN;
|
||||
if (*s == '.') {
|
||||
s += 1;
|
||||
if (!toint(&s, 3, &ms)) return NAN;
|
||||
}
|
||||
}
|
||||
if (*s == 'Z') {
|
||||
s += 1;
|
||||
tza = 0;
|
||||
} else if (*s == '+' || *s == '-') {
|
||||
int tzh = 0, tzm = 0;
|
||||
int tzs = *s == '+' ? 1 : -1;
|
||||
s += 1;
|
||||
if (!toint(&s, 2, &tzh)) return NAN;
|
||||
if (*s == ':') {
|
||||
s += 1;
|
||||
if (!toint(&s, 2, &tzm)) return NAN;
|
||||
}
|
||||
if (tzh > 23 || tzm > 59) return NAN;
|
||||
tza = tzs * (tzh * msPerHour + tzm * msPerMinute);
|
||||
} else {
|
||||
tza = LocalTZA();
|
||||
}
|
||||
}
|
||||
|
||||
if (*s) return NAN;
|
||||
|
||||
if (m < 1 || m > 12) return NAN;
|
||||
if (d < 1 || d > 31) return NAN;
|
||||
if (H < 0 || H > 24) return NAN;
|
||||
if (M < 0 || M > 59) return NAN;
|
||||
if (S < 0 || S > 59) return NAN;
|
||||
if (ms < 0 || ms > 999) return NAN;
|
||||
if (H == 24 && (M != 0 || S != 0 || ms != 0)) return NAN;
|
||||
|
||||
/* TODO: DaylightSavingTA on local times */
|
||||
t = MakeDate(MakeDay(y, m-1, d), MakeTime(H, M, S, ms));
|
||||
return t - tza;
|
||||
}
|
||||
|
||||
/* date formatting */
|
||||
|
||||
static char *fmtdate(char *buf, double t)
|
||||
{
|
||||
int y = YearFromTime(t);
|
||||
int m = MonthFromTime(t);
|
||||
int d = DateFromTime(t);
|
||||
if (!isfinite(t))
|
||||
return "Invalid Date";
|
||||
sprintf(buf, "%04d-%02d-%02d", y, m+1, d);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static char *fmttime(char *buf, double t, double tza)
|
||||
{
|
||||
int H = HourFromTime(t);
|
||||
int M = MinFromTime(t);
|
||||
int S = SecFromTime(t);
|
||||
int ms = msFromTime(t);
|
||||
int tzh = HourFromTime(fabs(tza));
|
||||
int tzm = MinFromTime(fabs(tza));
|
||||
if (!isfinite(t))
|
||||
return "Invalid Date";
|
||||
if (tza == 0)
|
||||
sprintf(buf, "%02d:%02d:%02d.%03dZ", H, M, S, ms);
|
||||
else if (tza < 0)
|
||||
sprintf(buf, "%02d:%02d:%02d.%03d-%02d:%02d", H, M, S, ms, tzh, tzm);
|
||||
else
|
||||
sprintf(buf, "%02d:%02d:%02d.%03d+%02d:%02d", H, M, S, ms, tzh, tzm);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static char *fmtdatetime(char *buf, double t, double tza)
|
||||
{
|
||||
char dbuf[20], tbuf[20];
|
||||
if (!isfinite(t))
|
||||
return "Invalid Date";
|
||||
fmtdate(dbuf, t);
|
||||
fmttime(tbuf, t, tza);
|
||||
sprintf(buf, "%sT%s", dbuf, tbuf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* Date functions */
|
||||
|
||||
static double js_todate(js_State *J, int idx)
|
||||
{
|
||||
js_Object *self = js_toobject(J, idx);
|
||||
if (self->type != JS_CDATE)
|
||||
js_typeerror(J, "not a date");
|
||||
return self->u.number;
|
||||
}
|
||||
|
||||
static void js_setdate(js_State *J, int idx, double t)
|
||||
{
|
||||
js_Object *self = js_toobject(J, idx);
|
||||
if (self->type != JS_CDATE)
|
||||
js_typeerror(J, "not a date");
|
||||
self->u.number = TimeClip(t);
|
||||
js_pushnumber(J, self->u.number);
|
||||
}
|
||||
|
||||
static void D_parse(js_State *J)
|
||||
{
|
||||
double t = parseDateTime(js_tostring(J, 1));
|
||||
js_pushnumber(J, t);
|
||||
}
|
||||
|
||||
static void D_UTC(js_State *J)
|
||||
{
|
||||
double y, m, d, H, M, S, ms, t;
|
||||
y = js_tonumber(J, 1);
|
||||
if (y < 100) y += 1900;
|
||||
m = js_tonumber(J, 2);
|
||||
d = js_optnumber(J, 3, 1);
|
||||
H = js_optnumber(J, 4, 0);
|
||||
M = js_optnumber(J, 5, 0);
|
||||
S = js_optnumber(J, 6, 0);
|
||||
ms = js_optnumber(J, 7, 0);
|
||||
t = MakeDate(MakeDay(y, m, d), MakeTime(H, M, S, ms));
|
||||
t = TimeClip(t);
|
||||
js_pushnumber(J, t);
|
||||
}
|
||||
|
||||
static void D_now(js_State *J)
|
||||
{
|
||||
js_pushnumber(J, Now());
|
||||
}
|
||||
|
||||
static void jsB_Date(js_State *J)
|
||||
{
|
||||
char buf[64];
|
||||
js_pushstring(J, fmtdatetime(buf, LocalTime(Now()), LocalTZA()));
|
||||
}
|
||||
|
||||
static void jsB_new_Date(js_State *J)
|
||||
{
|
||||
int top = js_gettop(J);
|
||||
js_Object *obj;
|
||||
double t;
|
||||
|
||||
if (top == 1)
|
||||
t = Now();
|
||||
else if (top == 2) {
|
||||
js_toprimitive(J, 1, JS_HNONE);
|
||||
if (js_isstring(J, 1))
|
||||
t = parseDateTime(js_tostring(J, 1));
|
||||
else
|
||||
t = TimeClip(js_tonumber(J, 1));
|
||||
} else {
|
||||
double y, m, d, H, M, S, ms;
|
||||
y = js_tonumber(J, 1);
|
||||
if (y < 100) y += 1900;
|
||||
m = js_tonumber(J, 2);
|
||||
d = js_optnumber(J, 3, 1);
|
||||
H = js_optnumber(J, 4, 0);
|
||||
M = js_optnumber(J, 5, 0);
|
||||
S = js_optnumber(J, 6, 0);
|
||||
ms = js_optnumber(J, 7, 0);
|
||||
t = MakeDate(MakeDay(y, m, d), MakeTime(H, M, S, ms));
|
||||
t = TimeClip(UTC(t));
|
||||
}
|
||||
|
||||
obj = jsV_newobject(J, JS_CDATE, J->Date_prototype);
|
||||
obj->u.number = t;
|
||||
|
||||
js_pushobject(J, obj);
|
||||
}
|
||||
|
||||
static void Dp_valueOf(js_State *J)
|
||||
{
|
||||
double t = js_todate(J, 0);
|
||||
js_pushnumber(J, t);
|
||||
}
|
||||
|
||||
static void Dp_toString(js_State *J)
|
||||
{
|
||||
char buf[64];
|
||||
double t = js_todate(J, 0);
|
||||
js_pushstring(J, fmtdatetime(buf, LocalTime(t), LocalTZA()));
|
||||
}
|
||||
|
||||
static void Dp_toDateString(js_State *J)
|
||||
{
|
||||
char buf[64];
|
||||
double t = js_todate(J, 0);
|
||||
js_pushstring(J, fmtdate(buf, LocalTime(t)));
|
||||
}
|
||||
|
||||
static void Dp_toTimeString(js_State *J)
|
||||
{
|
||||
char buf[64];
|
||||
double t = js_todate(J, 0);
|
||||
js_pushstring(J, fmttime(buf, LocalTime(t), LocalTZA()));
|
||||
}
|
||||
|
||||
static void Dp_toUTCString(js_State *J)
|
||||
{
|
||||
char buf[64];
|
||||
double t = js_todate(J, 0);
|
||||
js_pushstring(J, fmtdatetime(buf, t, 0));
|
||||
}
|
||||
|
||||
static void Dp_toISOString(js_State *J)
|
||||
{
|
||||
char buf[64];
|
||||
double t = js_todate(J, 0);
|
||||
if (!isfinite(t))
|
||||
js_rangeerror(J, "invalid date");
|
||||
js_pushstring(J, fmtdatetime(buf, t, 0));
|
||||
}
|
||||
|
||||
static void Dp_getFullYear(js_State *J)
|
||||
{
|
||||
double t = js_todate(J, 0);
|
||||
if (isnan(t))
|
||||
js_pushnumber(J, NAN);
|
||||
else
|
||||
js_pushnumber(J, YearFromTime(LocalTime(t)));
|
||||
}
|
||||
|
||||
static void Dp_getMonth(js_State *J)
|
||||
{
|
||||
double t = js_todate(J, 0);
|
||||
if (isnan(t))
|
||||
js_pushnumber(J, NAN);
|
||||
else
|
||||
js_pushnumber(J, MonthFromTime(LocalTime(t)));
|
||||
}
|
||||
|
||||
static void Dp_getDate(js_State *J)
|
||||
{
|
||||
double t = js_todate(J, 0);
|
||||
if (isnan(t))
|
||||
js_pushnumber(J, NAN);
|
||||
else
|
||||
js_pushnumber(J, DateFromTime(LocalTime(t)));
|
||||
}
|
||||
|
||||
static void Dp_getDay(js_State *J)
|
||||
{
|
||||
double t = js_todate(J, 0);
|
||||
if (isnan(t))
|
||||
js_pushnumber(J, NAN);
|
||||
else
|
||||
js_pushnumber(J, WeekDay(LocalTime(t)));
|
||||
}
|
||||
|
||||
static void Dp_getHours(js_State *J)
|
||||
{
|
||||
double t = js_todate(J, 0);
|
||||
if (isnan(t))
|
||||
js_pushnumber(J, NAN);
|
||||
else
|
||||
js_pushnumber(J, HourFromTime(LocalTime(t)));
|
||||
}
|
||||
|
||||
static void Dp_getMinutes(js_State *J)
|
||||
{
|
||||
double t = js_todate(J, 0);
|
||||
if (isnan(t))
|
||||
js_pushnumber(J, NAN);
|
||||
else
|
||||
js_pushnumber(J, MinFromTime(LocalTime(t)));
|
||||
}
|
||||
|
||||
static void Dp_getSeconds(js_State *J)
|
||||
{
|
||||
double t = js_todate(J, 0);
|
||||
if (isnan(t))
|
||||
js_pushnumber(J, NAN);
|
||||
else
|
||||
js_pushnumber(J, SecFromTime(LocalTime(t)));
|
||||
}
|
||||
|
||||
static void Dp_getMilliseconds(js_State *J)
|
||||
{
|
||||
double t = js_todate(J, 0);
|
||||
if (isnan(t))
|
||||
js_pushnumber(J, NAN);
|
||||
else
|
||||
js_pushnumber(J, msFromTime(LocalTime(t)));
|
||||
}
|
||||
|
||||
static void Dp_getUTCFullYear(js_State *J)
|
||||
{
|
||||
double t = js_todate(J, 0);
|
||||
if (isnan(t))
|
||||
js_pushnumber(J, NAN);
|
||||
else
|
||||
js_pushnumber(J, YearFromTime(t));
|
||||
}
|
||||
|
||||
static void Dp_getUTCMonth(js_State *J)
|
||||
{
|
||||
double t = js_todate(J, 0);
|
||||
if (isnan(t))
|
||||
js_pushnumber(J, NAN);
|
||||
else
|
||||
js_pushnumber(J, MonthFromTime(t));
|
||||
}
|
||||
|
||||
static void Dp_getUTCDate(js_State *J)
|
||||
{
|
||||
double t = js_todate(J, 0);
|
||||
if (isnan(t))
|
||||
js_pushnumber(J, NAN);
|
||||
else
|
||||
js_pushnumber(J, DateFromTime(t));
|
||||
}
|
||||
|
||||
static void Dp_getUTCDay(js_State *J)
|
||||
{
|
||||
double t = js_todate(J, 0);
|
||||
if (isnan(t))
|
||||
js_pushnumber(J, NAN);
|
||||
else
|
||||
js_pushnumber(J, WeekDay(t));
|
||||
}
|
||||
|
||||
static void Dp_getUTCHours(js_State *J)
|
||||
{
|
||||
double t = js_todate(J, 0);
|
||||
if (isnan(t))
|
||||
js_pushnumber(J, NAN);
|
||||
else
|
||||
js_pushnumber(J, HourFromTime(t));
|
||||
}
|
||||
|
||||
static void Dp_getUTCMinutes(js_State *J)
|
||||
{
|
||||
double t = js_todate(J, 0);
|
||||
if (isnan(t))
|
||||
js_pushnumber(J, NAN);
|
||||
else
|
||||
js_pushnumber(J, MinFromTime(t));
|
||||
}
|
||||
|
||||
static void Dp_getUTCSeconds(js_State *J)
|
||||
{
|
||||
double t = js_todate(J, 0);
|
||||
if (isnan(t))
|
||||
js_pushnumber(J, NAN);
|
||||
else
|
||||
js_pushnumber(J, SecFromTime(t));
|
||||
}
|
||||
|
||||
static void Dp_getUTCMilliseconds(js_State *J)
|
||||
{
|
||||
double t = js_todate(J, 0);
|
||||
if (isnan(t))
|
||||
js_pushnumber(J, NAN);
|
||||
else
|
||||
js_pushnumber(J, msFromTime(t));
|
||||
}
|
||||
|
||||
static void Dp_getTimezoneOffset(js_State *J)
|
||||
{
|
||||
double t = js_todate(J, 0);
|
||||
if (isnan(t))
|
||||
js_pushnumber(J, NAN);
|
||||
else
|
||||
js_pushnumber(J, (t - LocalTime(t)) / msPerMinute);
|
||||
}
|
||||
|
||||
static void Dp_setTime(js_State *J)
|
||||
{
|
||||
js_setdate(J, 0, js_tonumber(J, 1));
|
||||
}
|
||||
|
||||
static void Dp_setMilliseconds(js_State *J)
|
||||
{
|
||||
double t = LocalTime(js_todate(J, 0));
|
||||
double h = HourFromTime(t);
|
||||
double m = MinFromTime(t);
|
||||
double s = SecFromTime(t);
|
||||
double ms = js_tonumber(J, 1);
|
||||
js_setdate(J, 0, UTC(MakeDate(Day(t), MakeTime(h, m, s, ms))));
|
||||
}
|
||||
|
||||
static void Dp_setSeconds(js_State *J)
|
||||
{
|
||||
double t = LocalTime(js_todate(J, 0));
|
||||
double h = HourFromTime(t);
|
||||
double m = MinFromTime(t);
|
||||
double s = js_tonumber(J, 1);
|
||||
double ms = js_optnumber(J, 2, msFromTime(t));
|
||||
js_setdate(J, 0, UTC(MakeDate(Day(t), MakeTime(h, m, s, ms))));
|
||||
}
|
||||
|
||||
static void Dp_setMinutes(js_State *J)
|
||||
{
|
||||
double t = LocalTime(js_todate(J, 0));
|
||||
double h = HourFromTime(t);
|
||||
double m = js_tonumber(J, 1);
|
||||
double s = js_optnumber(J, 2, SecFromTime(t));
|
||||
double ms = js_optnumber(J, 3, msFromTime(t));
|
||||
js_setdate(J, 0, UTC(MakeDate(Day(t), MakeTime(h, m, s, ms))));
|
||||
}
|
||||
|
||||
static void Dp_setHours(js_State *J)
|
||||
{
|
||||
double t = LocalTime(js_todate(J, 0));
|
||||
double h = js_tonumber(J, 1);
|
||||
double m = js_optnumber(J, 2, MinFromTime(t));
|
||||
double s = js_optnumber(J, 3, SecFromTime(t));
|
||||
double ms = js_optnumber(J, 4, msFromTime(t));
|
||||
js_setdate(J, 0, UTC(MakeDate(Day(t), MakeTime(h, m, s, ms))));
|
||||
}
|
||||
|
||||
static void Dp_setDate(js_State *J)
|
||||
{
|
||||
double t = LocalTime(js_todate(J, 0));
|
||||
double y = YearFromTime(t);
|
||||
double m = MonthFromTime(t);
|
||||
double d = js_tonumber(J, 1);
|
||||
js_setdate(J, 0, UTC(MakeDate(MakeDay(y, m, d), TimeWithinDay(t))));
|
||||
}
|
||||
|
||||
static void Dp_setMonth(js_State *J)
|
||||
{
|
||||
double t = LocalTime(js_todate(J, 0));
|
||||
double y = YearFromTime(t);
|
||||
double m = js_tonumber(J, 1);
|
||||
double d = js_optnumber(J, 2, DateFromTime(t));
|
||||
js_setdate(J, 0, UTC(MakeDate(MakeDay(y, m, d), TimeWithinDay(t))));
|
||||
}
|
||||
|
||||
static void Dp_setFullYear(js_State *J)
|
||||
{
|
||||
double t = LocalTime(js_todate(J, 0));
|
||||
double y = js_tonumber(J, 1);
|
||||
double m = js_optnumber(J, 2, MonthFromTime(t));
|
||||
double d = js_optnumber(J, 3, DateFromTime(t));
|
||||
js_setdate(J, 0, UTC(MakeDate(MakeDay(y, m, d), TimeWithinDay(t))));
|
||||
}
|
||||
|
||||
static void Dp_setUTCMilliseconds(js_State *J)
|
||||
{
|
||||
double t = js_todate(J, 0);
|
||||
double h = HourFromTime(t);
|
||||
double m = MinFromTime(t);
|
||||
double s = SecFromTime(t);
|
||||
double ms = js_tonumber(J, 1);
|
||||
js_setdate(J, 0, MakeDate(Day(t), MakeTime(h, m, s, ms)));
|
||||
}
|
||||
|
||||
static void Dp_setUTCSeconds(js_State *J)
|
||||
{
|
||||
double t = js_todate(J, 0);
|
||||
double h = HourFromTime(t);
|
||||
double m = MinFromTime(t);
|
||||
double s = js_tonumber(J, 1);
|
||||
double ms = js_optnumber(J, 2, msFromTime(t));
|
||||
js_setdate(J, 0, MakeDate(Day(t), MakeTime(h, m, s, ms)));
|
||||
}
|
||||
|
||||
static void Dp_setUTCMinutes(js_State *J)
|
||||
{
|
||||
double t = js_todate(J, 0);
|
||||
double h = HourFromTime(t);
|
||||
double m = js_tonumber(J, 1);
|
||||
double s = js_optnumber(J, 2, SecFromTime(t));
|
||||
double ms = js_optnumber(J, 3, msFromTime(t));
|
||||
js_setdate(J, 0, MakeDate(Day(t), MakeTime(h, m, s, ms)));
|
||||
}
|
||||
|
||||
static void Dp_setUTCHours(js_State *J)
|
||||
{
|
||||
double t = js_todate(J, 0);
|
||||
double h = js_tonumber(J, 1);
|
||||
double m = js_optnumber(J, 2, HourFromTime(t));
|
||||
double s = js_optnumber(J, 3, SecFromTime(t));
|
||||
double ms = js_optnumber(J, 4, msFromTime(t));
|
||||
js_setdate(J, 0, MakeDate(Day(t), MakeTime(h, m, s, ms)));
|
||||
}
|
||||
|
||||
static void Dp_setUTCDate(js_State *J)
|
||||
{
|
||||
double t = js_todate(J, 0);
|
||||
double y = YearFromTime(t);
|
||||
double m = MonthFromTime(t);
|
||||
double d = js_tonumber(J, 1);
|
||||
js_setdate(J, 0, MakeDate(MakeDay(y, m, d), TimeWithinDay(t)));
|
||||
}
|
||||
|
||||
static void Dp_setUTCMonth(js_State *J)
|
||||
{
|
||||
double t = js_todate(J, 0);
|
||||
double y = YearFromTime(t);
|
||||
double m = js_tonumber(J, 1);
|
||||
double d = js_optnumber(J, 2, DateFromTime(t));
|
||||
js_setdate(J, 0, MakeDate(MakeDay(y, m, d), TimeWithinDay(t)));
|
||||
}
|
||||
|
||||
static void Dp_setUTCFullYear(js_State *J)
|
||||
{
|
||||
double t = js_todate(J, 0);
|
||||
double y = js_tonumber(J, 1);
|
||||
double m = js_optnumber(J, 2, MonthFromTime(t));
|
||||
double d = js_optnumber(J, 3, DateFromTime(t));
|
||||
js_setdate(J, 0, MakeDate(MakeDay(y, m, d), TimeWithinDay(t)));
|
||||
}
|
||||
|
||||
static void Dp_toJSON(js_State *J)
|
||||
{
|
||||
js_copy(J, 0);
|
||||
js_toprimitive(J, -1, JS_HNUMBER);
|
||||
if (js_isnumber(J, -1) && !isfinite(js_tonumber(J, -1))) {
|
||||
js_pushnull(J);
|
||||
return;
|
||||
}
|
||||
js_pop(J, 1);
|
||||
|
||||
js_getproperty(J, 0, "toISOString");
|
||||
if (!js_iscallable(J, -1))
|
||||
js_typeerror(J, "this.toISOString is not a function");
|
||||
js_copy(J, 0);
|
||||
js_call(J, 0);
|
||||
}
|
||||
|
||||
void jsB_initdate(js_State *J)
|
||||
{
|
||||
J->Date_prototype->u.number = 0;
|
||||
|
||||
js_pushobject(J, J->Date_prototype);
|
||||
{
|
||||
jsB_propf(J, "Date.prototype.valueOf", Dp_valueOf, 0);
|
||||
jsB_propf(J, "Date.prototype.toString", Dp_toString, 0);
|
||||
jsB_propf(J, "Date.prototype.toDateString", Dp_toDateString, 0);
|
||||
jsB_propf(J, "Date.prototype.toTimeString", Dp_toTimeString, 0);
|
||||
jsB_propf(J, "Date.prototype.toLocaleString", Dp_toString, 0);
|
||||
jsB_propf(J, "Date.prototype.toLocaleDateString", Dp_toDateString, 0);
|
||||
jsB_propf(J, "Date.prototype.toLocaleTimeString", Dp_toTimeString, 0);
|
||||
jsB_propf(J, "Date.prototype.toUTCString", Dp_toUTCString, 0);
|
||||
|
||||
jsB_propf(J, "Date.prototype.getTime", Dp_valueOf, 0);
|
||||
jsB_propf(J, "Date.prototype.getFullYear", Dp_getFullYear, 0);
|
||||
jsB_propf(J, "Date.prototype.getUTCFullYear", Dp_getUTCFullYear, 0);
|
||||
jsB_propf(J, "Date.prototype.getMonth", Dp_getMonth, 0);
|
||||
jsB_propf(J, "Date.prototype.getUTCMonth", Dp_getUTCMonth, 0);
|
||||
jsB_propf(J, "Date.prototype.getDate", Dp_getDate, 0);
|
||||
jsB_propf(J, "Date.prototype.getUTCDate", Dp_getUTCDate, 0);
|
||||
jsB_propf(J, "Date.prototype.getDay", Dp_getDay, 0);
|
||||
jsB_propf(J, "Date.prototype.getUTCDay", Dp_getUTCDay, 0);
|
||||
jsB_propf(J, "Date.prototype.getHours", Dp_getHours, 0);
|
||||
jsB_propf(J, "Date.prototype.getUTCHours", Dp_getUTCHours, 0);
|
||||
jsB_propf(J, "Date.prototype.getMinutes", Dp_getMinutes, 0);
|
||||
jsB_propf(J, "Date.prototype.getUTCMinutes", Dp_getUTCMinutes, 0);
|
||||
jsB_propf(J, "Date.prototype.getSeconds", Dp_getSeconds, 0);
|
||||
jsB_propf(J, "Date.prototype.getUTCSeconds", Dp_getUTCSeconds, 0);
|
||||
jsB_propf(J, "Date.prototype.getMilliseconds", Dp_getMilliseconds, 0);
|
||||
jsB_propf(J, "Date.prototype.getUTCMilliseconds", Dp_getUTCMilliseconds, 0);
|
||||
jsB_propf(J, "Date.prototype.getTimezoneOffset", Dp_getTimezoneOffset, 0);
|
||||
|
||||
jsB_propf(J, "Date.prototype.setTime", Dp_setTime, 1);
|
||||
jsB_propf(J, "Date.prototype.setMilliseconds", Dp_setMilliseconds, 1);
|
||||
jsB_propf(J, "Date.prototype.setUTCMilliseconds", Dp_setUTCMilliseconds, 1);
|
||||
jsB_propf(J, "Date.prototype.setSeconds", Dp_setSeconds, 2);
|
||||
jsB_propf(J, "Date.prototype.setUTCSeconds", Dp_setUTCSeconds, 2);
|
||||
jsB_propf(J, "Date.prototype.setMinutes", Dp_setMinutes, 3);
|
||||
jsB_propf(J, "Date.prototype.setUTCMinutes", Dp_setUTCMinutes, 3);
|
||||
jsB_propf(J, "Date.prototype.setHours", Dp_setHours, 4);
|
||||
jsB_propf(J, "Date.prototype.setUTCHours", Dp_setUTCHours, 4);
|
||||
jsB_propf(J, "Date.prototype.setDate", Dp_setDate, 1);
|
||||
jsB_propf(J, "Date.prototype.setUTCDate", Dp_setUTCDate, 1);
|
||||
jsB_propf(J, "Date.prototype.setMonth", Dp_setMonth, 2);
|
||||
jsB_propf(J, "Date.prototype.setUTCMonth", Dp_setUTCMonth, 2);
|
||||
jsB_propf(J, "Date.prototype.setFullYear", Dp_setFullYear, 3);
|
||||
jsB_propf(J, "Date.prototype.setUTCFullYear", Dp_setUTCFullYear, 3);
|
||||
|
||||
/* ES5 */
|
||||
jsB_propf(J, "Date.prototype.toISOString", Dp_toISOString, 0);
|
||||
jsB_propf(J, "Date.prototype.toJSON", Dp_toJSON, 1);
|
||||
}
|
||||
js_newcconstructor(J, jsB_Date, jsB_new_Date, "Date", 0); /* 1 */
|
||||
{
|
||||
jsB_propf(J, "Date.parse", D_parse, 1);
|
||||
jsB_propf(J, "Date.UTC", D_UTC, 7);
|
||||
|
||||
/* ES5 */
|
||||
jsB_propf(J, "Date.now", D_now, 0);
|
||||
}
|
||||
js_defglobal(J, "Date", JS_DONTENUM);
|
||||
}
|
749
src/mujs/jsdtoa.c
Normal file
749
src/mujs/jsdtoa.c
Normal file
|
@ -0,0 +1,749 @@
|
|||
/* Locale-independent implementations of string <-> double conversions. */
|
||||
|
||||
#include "jsi.h"
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1700) /* VS2012 has stdint.h */
|
||||
typedef unsigned int uint32_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
* format exponent like sprintf(p, "e%+d", e)
|
||||
*/
|
||||
void
|
||||
js_fmtexp(char *p, int e)
|
||||
{
|
||||
char se[9];
|
||||
int i;
|
||||
|
||||
*p++ = 'e';
|
||||
if(e < 0) {
|
||||
*p++ = '-';
|
||||
e = -e;
|
||||
} else
|
||||
*p++ = '+';
|
||||
i = 0;
|
||||
while(e) {
|
||||
se[i++] = e % 10 + '0';
|
||||
e /= 10;
|
||||
}
|
||||
while(i < 1)
|
||||
se[i++] = '0';
|
||||
while(i > 0)
|
||||
*p++ = se[--i];
|
||||
*p++ = '\0';
|
||||
}
|
||||
|
||||
/*
|
||||
* grisu2_59_56.c
|
||||
*
|
||||
* Grisu prints the optimal decimal representation of floating-point numbers.
|
||||
*
|
||||
* Copyright (c) 2009 Florian Loitsch
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
typedef struct diy_fp_t {
|
||||
uint64_t f;
|
||||
int e;
|
||||
} diy_fp_t;
|
||||
|
||||
#define DIY_SIGNIFICAND_SIZE 64
|
||||
#define D_1_LOG2_10 0.30102999566398114 /* 1 / lg(10) */
|
||||
|
||||
static const uint64_t powers_ten[] = {
|
||||
0xbf29dcaba82fdeae, 0xeef453d6923bd65a, 0x9558b4661b6565f8,
|
||||
0xbaaee17fa23ebf76, 0xe95a99df8ace6f54, 0x91d8a02bb6c10594,
|
||||
0xb64ec836a47146fa, 0xe3e27a444d8d98b8, 0x8e6d8c6ab0787f73,
|
||||
0xb208ef855c969f50, 0xde8b2b66b3bc4724, 0x8b16fb203055ac76,
|
||||
0xaddcb9e83c6b1794, 0xd953e8624b85dd79, 0x87d4713d6f33aa6c,
|
||||
0xa9c98d8ccb009506, 0xd43bf0effdc0ba48, 0x84a57695fe98746d,
|
||||
0xa5ced43b7e3e9188, 0xcf42894a5dce35ea, 0x818995ce7aa0e1b2,
|
||||
0xa1ebfb4219491a1f, 0xca66fa129f9b60a7, 0xfd00b897478238d1,
|
||||
0x9e20735e8cb16382, 0xc5a890362fddbc63, 0xf712b443bbd52b7c,
|
||||
0x9a6bb0aa55653b2d, 0xc1069cd4eabe89f9, 0xf148440a256e2c77,
|
||||
0x96cd2a865764dbca, 0xbc807527ed3e12bd, 0xeba09271e88d976c,
|
||||
0x93445b8731587ea3, 0xb8157268fdae9e4c, 0xe61acf033d1a45df,
|
||||
0x8fd0c16206306bac, 0xb3c4f1ba87bc8697, 0xe0b62e2929aba83c,
|
||||
0x8c71dcd9ba0b4926, 0xaf8e5410288e1b6f, 0xdb71e91432b1a24b,
|
||||
0x892731ac9faf056f, 0xab70fe17c79ac6ca, 0xd64d3d9db981787d,
|
||||
0x85f0468293f0eb4e, 0xa76c582338ed2622, 0xd1476e2c07286faa,
|
||||
0x82cca4db847945ca, 0xa37fce126597973d, 0xcc5fc196fefd7d0c,
|
||||
0xff77b1fcbebcdc4f, 0x9faacf3df73609b1, 0xc795830d75038c1e,
|
||||
0xf97ae3d0d2446f25, 0x9becce62836ac577, 0xc2e801fb244576d5,
|
||||
0xf3a20279ed56d48a, 0x9845418c345644d7, 0xbe5691ef416bd60c,
|
||||
0xedec366b11c6cb8f, 0x94b3a202eb1c3f39, 0xb9e08a83a5e34f08,
|
||||
0xe858ad248f5c22ca, 0x91376c36d99995be, 0xb58547448ffffb2e,
|
||||
0xe2e69915b3fff9f9, 0x8dd01fad907ffc3c, 0xb1442798f49ffb4b,
|
||||
0xdd95317f31c7fa1d, 0x8a7d3eef7f1cfc52, 0xad1c8eab5ee43b67,
|
||||
0xd863b256369d4a41, 0x873e4f75e2224e68, 0xa90de3535aaae202,
|
||||
0xd3515c2831559a83, 0x8412d9991ed58092, 0xa5178fff668ae0b6,
|
||||
0xce5d73ff402d98e4, 0x80fa687f881c7f8e, 0xa139029f6a239f72,
|
||||
0xc987434744ac874f, 0xfbe9141915d7a922, 0x9d71ac8fada6c9b5,
|
||||
0xc4ce17b399107c23, 0xf6019da07f549b2b, 0x99c102844f94e0fb,
|
||||
0xc0314325637a193a, 0xf03d93eebc589f88, 0x96267c7535b763b5,
|
||||
0xbbb01b9283253ca3, 0xea9c227723ee8bcb, 0x92a1958a7675175f,
|
||||
0xb749faed14125d37, 0xe51c79a85916f485, 0x8f31cc0937ae58d3,
|
||||
0xb2fe3f0b8599ef08, 0xdfbdcece67006ac9, 0x8bd6a141006042be,
|
||||
0xaecc49914078536d, 0xda7f5bf590966849, 0x888f99797a5e012d,
|
||||
0xaab37fd7d8f58179, 0xd5605fcdcf32e1d7, 0x855c3be0a17fcd26,
|
||||
0xa6b34ad8c9dfc070, 0xd0601d8efc57b08c, 0x823c12795db6ce57,
|
||||
0xa2cb1717b52481ed, 0xcb7ddcdda26da269, 0xfe5d54150b090b03,
|
||||
0x9efa548d26e5a6e2, 0xc6b8e9b0709f109a, 0xf867241c8cc6d4c1,
|
||||
0x9b407691d7fc44f8, 0xc21094364dfb5637, 0xf294b943e17a2bc4,
|
||||
0x979cf3ca6cec5b5b, 0xbd8430bd08277231, 0xece53cec4a314ebe,
|
||||
0x940f4613ae5ed137, 0xb913179899f68584, 0xe757dd7ec07426e5,
|
||||
0x9096ea6f3848984f, 0xb4bca50b065abe63, 0xe1ebce4dc7f16dfc,
|
||||
0x8d3360f09cf6e4bd, 0xb080392cc4349ded, 0xdca04777f541c568,
|
||||
0x89e42caaf9491b61, 0xac5d37d5b79b6239, 0xd77485cb25823ac7,
|
||||
0x86a8d39ef77164bd, 0xa8530886b54dbdec, 0xd267caa862a12d67,
|
||||
0x8380dea93da4bc60, 0xa46116538d0deb78, 0xcd795be870516656,
|
||||
0x806bd9714632dff6, 0xa086cfcd97bf97f4, 0xc8a883c0fdaf7df0,
|
||||
0xfad2a4b13d1b5d6c, 0x9cc3a6eec6311a64, 0xc3f490aa77bd60fd,
|
||||
0xf4f1b4d515acb93c, 0x991711052d8bf3c5, 0xbf5cd54678eef0b7,
|
||||
0xef340a98172aace5, 0x9580869f0e7aac0f, 0xbae0a846d2195713,
|
||||
0xe998d258869facd7, 0x91ff83775423cc06, 0xb67f6455292cbf08,
|
||||
0xe41f3d6a7377eeca, 0x8e938662882af53e, 0xb23867fb2a35b28e,
|
||||
0xdec681f9f4c31f31, 0x8b3c113c38f9f37f, 0xae0b158b4738705f,
|
||||
0xd98ddaee19068c76, 0x87f8a8d4cfa417ca, 0xa9f6d30a038d1dbc,
|
||||
0xd47487cc8470652b, 0x84c8d4dfd2c63f3b, 0xa5fb0a17c777cf0a,
|
||||
0xcf79cc9db955c2cc, 0x81ac1fe293d599c0, 0xa21727db38cb0030,
|
||||
0xca9cf1d206fdc03c, 0xfd442e4688bd304b, 0x9e4a9cec15763e2f,
|
||||
0xc5dd44271ad3cdba, 0xf7549530e188c129, 0x9a94dd3e8cf578ba,
|
||||
0xc13a148e3032d6e8, 0xf18899b1bc3f8ca2, 0x96f5600f15a7b7e5,
|
||||
0xbcb2b812db11a5de, 0xebdf661791d60f56, 0x936b9fcebb25c996,
|
||||
0xb84687c269ef3bfb, 0xe65829b3046b0afa, 0x8ff71a0fe2c2e6dc,
|
||||
0xb3f4e093db73a093, 0xe0f218b8d25088b8, 0x8c974f7383725573,
|
||||
0xafbd2350644eead0, 0xdbac6c247d62a584, 0x894bc396ce5da772,
|
||||
0xab9eb47c81f5114f, 0xd686619ba27255a3, 0x8613fd0145877586,
|
||||
0xa798fc4196e952e7, 0xd17f3b51fca3a7a1, 0x82ef85133de648c5,
|
||||
0xa3ab66580d5fdaf6, 0xcc963fee10b7d1b3, 0xffbbcfe994e5c620,
|
||||
0x9fd561f1fd0f9bd4, 0xc7caba6e7c5382c9, 0xf9bd690a1b68637b,
|
||||
0x9c1661a651213e2d, 0xc31bfa0fe5698db8, 0xf3e2f893dec3f126,
|
||||
0x986ddb5c6b3a76b8, 0xbe89523386091466, 0xee2ba6c0678b597f,
|
||||
0x94db483840b717f0, 0xba121a4650e4ddec, 0xe896a0d7e51e1566,
|
||||
0x915e2486ef32cd60, 0xb5b5ada8aaff80b8, 0xe3231912d5bf60e6,
|
||||
0x8df5efabc5979c90, 0xb1736b96b6fd83b4, 0xddd0467c64bce4a1,
|
||||
0x8aa22c0dbef60ee4, 0xad4ab7112eb3929e, 0xd89d64d57a607745,
|
||||
0x87625f056c7c4a8b, 0xa93af6c6c79b5d2e, 0xd389b47879823479,
|
||||
0x843610cb4bf160cc, 0xa54394fe1eedb8ff, 0xce947a3da6a9273e,
|
||||
0x811ccc668829b887, 0xa163ff802a3426a9, 0xc9bcff6034c13053,
|
||||
0xfc2c3f3841f17c68, 0x9d9ba7832936edc1, 0xc5029163f384a931,
|
||||
0xf64335bcf065d37d, 0x99ea0196163fa42e, 0xc06481fb9bcf8d3a,
|
||||
0xf07da27a82c37088, 0x964e858c91ba2655, 0xbbe226efb628afeb,
|
||||
0xeadab0aba3b2dbe5, 0x92c8ae6b464fc96f, 0xb77ada0617e3bbcb,
|
||||
0xe55990879ddcaabe, 0x8f57fa54c2a9eab7, 0xb32df8e9f3546564,
|
||||
0xdff9772470297ebd, 0x8bfbea76c619ef36, 0xaefae51477a06b04,
|
||||
0xdab99e59958885c5, 0x88b402f7fd75539b, 0xaae103b5fcd2a882,
|
||||
0xd59944a37c0752a2, 0x857fcae62d8493a5, 0xa6dfbd9fb8e5b88f,
|
||||
0xd097ad07a71f26b2, 0x825ecc24c8737830, 0xa2f67f2dfa90563b,
|
||||
0xcbb41ef979346bca, 0xfea126b7d78186bd, 0x9f24b832e6b0f436,
|
||||
0xc6ede63fa05d3144, 0xf8a95fcf88747d94, 0x9b69dbe1b548ce7d,
|
||||
0xc24452da229b021c, 0xf2d56790ab41c2a3, 0x97c560ba6b0919a6,
|
||||
0xbdb6b8e905cb600f, 0xed246723473e3813, 0x9436c0760c86e30c,
|
||||
0xb94470938fa89bcf, 0xe7958cb87392c2c3, 0x90bd77f3483bb9ba,
|
||||
0xb4ecd5f01a4aa828, 0xe2280b6c20dd5232, 0x8d590723948a535f,
|
||||
0xb0af48ec79ace837, 0xdcdb1b2798182245, 0x8a08f0f8bf0f156b,
|
||||
0xac8b2d36eed2dac6, 0xd7adf884aa879177, 0x86ccbb52ea94baeb,
|
||||
0xa87fea27a539e9a5, 0xd29fe4b18e88640f, 0x83a3eeeef9153e89,
|
||||
0xa48ceaaab75a8e2b, 0xcdb02555653131b6, 0x808e17555f3ebf12,
|
||||
0xa0b19d2ab70e6ed6, 0xc8de047564d20a8c, 0xfb158592be068d2f,
|
||||
0x9ced737bb6c4183d, 0xc428d05aa4751e4d, 0xf53304714d9265e0,
|
||||
0x993fe2c6d07b7fac, 0xbf8fdb78849a5f97, 0xef73d256a5c0f77d,
|
||||
0x95a8637627989aae, 0xbb127c53b17ec159, 0xe9d71b689dde71b0,
|
||||
0x9226712162ab070e, 0xb6b00d69bb55c8d1, 0xe45c10c42a2b3b06,
|
||||
0x8eb98a7a9a5b04e3, 0xb267ed1940f1c61c, 0xdf01e85f912e37a3,
|
||||
0x8b61313bbabce2c6, 0xae397d8aa96c1b78, 0xd9c7dced53c72256,
|
||||
0x881cea14545c7575, 0xaa242499697392d3, 0xd4ad2dbfc3d07788,
|
||||
0x84ec3c97da624ab5, 0xa6274bbdd0fadd62, 0xcfb11ead453994ba,
|
||||
0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3e,
|
||||
0xfd87b5f28300ca0e, 0x9e74d1b791e07e48, 0xc612062576589ddb,
|
||||
0xf79687aed3eec551, 0x9abe14cd44753b53, 0xc16d9a0095928a27,
|
||||
0xf1c90080baf72cb1, 0x971da05074da7bef, 0xbce5086492111aeb,
|
||||
0xec1e4a7db69561a5, 0x9392ee8e921d5d07, 0xb877aa3236a4b449,
|
||||
0xe69594bec44de15b, 0x901d7cf73ab0acd9, 0xb424dc35095cd80f,
|
||||
0xe12e13424bb40e13, 0x8cbccc096f5088cc, 0xafebff0bcb24aaff,
|
||||
0xdbe6fecebdedd5bf, 0x89705f4136b4a597, 0xabcc77118461cefd,
|
||||
0xd6bf94d5e57a42bc, 0x8637bd05af6c69b6, 0xa7c5ac471b478423,
|
||||
0xd1b71758e219652c, 0x83126e978d4fdf3b, 0xa3d70a3d70a3d70a,
|
||||
0xcccccccccccccccd, 0x8000000000000000, 0xa000000000000000,
|
||||
0xc800000000000000, 0xfa00000000000000, 0x9c40000000000000,
|
||||
0xc350000000000000, 0xf424000000000000, 0x9896800000000000,
|
||||
0xbebc200000000000, 0xee6b280000000000, 0x9502f90000000000,
|
||||
0xba43b74000000000, 0xe8d4a51000000000, 0x9184e72a00000000,
|
||||
0xb5e620f480000000, 0xe35fa931a0000000, 0x8e1bc9bf04000000,
|
||||
0xb1a2bc2ec5000000, 0xde0b6b3a76400000, 0x8ac7230489e80000,
|
||||
0xad78ebc5ac620000, 0xd8d726b7177a8000, 0x878678326eac9000,
|
||||
0xa968163f0a57b400, 0xd3c21bcecceda100, 0x84595161401484a0,
|
||||
0xa56fa5b99019a5c8, 0xcecb8f27f4200f3a, 0x813f3978f8940984,
|
||||
0xa18f07d736b90be5, 0xc9f2c9cd04674edf, 0xfc6f7c4045812296,
|
||||
0x9dc5ada82b70b59e, 0xc5371912364ce305, 0xf684df56c3e01bc7,
|
||||
0x9a130b963a6c115c, 0xc097ce7bc90715b3, 0xf0bdc21abb48db20,
|
||||
0x96769950b50d88f4, 0xbc143fa4e250eb31, 0xeb194f8e1ae525fd,
|
||||
0x92efd1b8d0cf37be, 0xb7abc627050305ae, 0xe596b7b0c643c719,
|
||||
0x8f7e32ce7bea5c70, 0xb35dbf821ae4f38c, 0xe0352f62a19e306f,
|
||||
0x8c213d9da502de45, 0xaf298d050e4395d7, 0xdaf3f04651d47b4c,
|
||||
0x88d8762bf324cd10, 0xab0e93b6efee0054, 0xd5d238a4abe98068,
|
||||
0x85a36366eb71f041, 0xa70c3c40a64e6c52, 0xd0cf4b50cfe20766,
|
||||
0x82818f1281ed44a0, 0xa321f2d7226895c8, 0xcbea6f8ceb02bb3a,
|
||||
0xfee50b7025c36a08, 0x9f4f2726179a2245, 0xc722f0ef9d80aad6,
|
||||
0xf8ebad2b84e0d58c, 0x9b934c3b330c8577, 0xc2781f49ffcfa6d5,
|
||||
0xf316271c7fc3908b, 0x97edd871cfda3a57, 0xbde94e8e43d0c8ec,
|
||||
0xed63a231d4c4fb27, 0x945e455f24fb1cf9, 0xb975d6b6ee39e437,
|
||||
0xe7d34c64a9c85d44, 0x90e40fbeea1d3a4b, 0xb51d13aea4a488dd,
|
||||
0xe264589a4dcdab15, 0x8d7eb76070a08aed, 0xb0de65388cc8ada8,
|
||||
0xdd15fe86affad912, 0x8a2dbf142dfcc7ab, 0xacb92ed9397bf996,
|
||||
0xd7e77a8f87daf7fc, 0x86f0ac99b4e8dafd, 0xa8acd7c0222311bd,
|
||||
0xd2d80db02aabd62c, 0x83c7088e1aab65db, 0xa4b8cab1a1563f52,
|
||||
0xcde6fd5e09abcf27, 0x80b05e5ac60b6178, 0xa0dc75f1778e39d6,
|
||||
0xc913936dd571c84c, 0xfb5878494ace3a5f, 0x9d174b2dcec0e47b,
|
||||
0xc45d1df942711d9a, 0xf5746577930d6501, 0x9968bf6abbe85f20,
|
||||
0xbfc2ef456ae276e9, 0xefb3ab16c59b14a3, 0x95d04aee3b80ece6,
|
||||
0xbb445da9ca61281f, 0xea1575143cf97227, 0x924d692ca61be758,
|
||||
0xb6e0c377cfa2e12e, 0xe498f455c38b997a, 0x8edf98b59a373fec,
|
||||
0xb2977ee300c50fe7, 0xdf3d5e9bc0f653e1, 0x8b865b215899f46d,
|
||||
0xae67f1e9aec07188, 0xda01ee641a708dea, 0x884134fe908658b2,
|
||||
0xaa51823e34a7eedf, 0xd4e5e2cdc1d1ea96, 0x850fadc09923329e,
|
||||
0xa6539930bf6bff46, 0xcfe87f7cef46ff17, 0x81f14fae158c5f6e,
|
||||
0xa26da3999aef774a, 0xcb090c8001ab551c, 0xfdcb4fa002162a63,
|
||||
0x9e9f11c4014dda7e, 0xc646d63501a1511e, 0xf7d88bc24209a565,
|
||||
0x9ae757596946075f, 0xc1a12d2fc3978937, 0xf209787bb47d6b85,
|
||||
0x9745eb4d50ce6333, 0xbd176620a501fc00, 0xec5d3fa8ce427b00,
|
||||
0x93ba47c980e98ce0, 0xb8a8d9bbe123f018, 0xe6d3102ad96cec1e,
|
||||
0x9043ea1ac7e41393, 0xb454e4a179dd1877, 0xe16a1dc9d8545e95,
|
||||
0x8ce2529e2734bb1d, 0xb01ae745b101e9e4, 0xdc21a1171d42645d,
|
||||
0x899504ae72497eba, 0xabfa45da0edbde69, 0xd6f8d7509292d603,
|
||||
0x865b86925b9bc5c2, 0xa7f26836f282b733, 0xd1ef0244af2364ff,
|
||||
0x8335616aed761f1f, 0xa402b9c5a8d3a6e7, 0xcd036837130890a1,
|
||||
0x802221226be55a65, 0xa02aa96b06deb0fe, 0xc83553c5c8965d3d,
|
||||
0xfa42a8b73abbf48d, 0x9c69a97284b578d8, 0xc38413cf25e2d70e,
|
||||
0xf46518c2ef5b8cd1, 0x98bf2f79d5993803, 0xbeeefb584aff8604,
|
||||
0xeeaaba2e5dbf6785, 0x952ab45cfa97a0b3, 0xba756174393d88e0,
|
||||
0xe912b9d1478ceb17, 0x91abb422ccb812ef, 0xb616a12b7fe617aa,
|
||||
0xe39c49765fdf9d95, 0x8e41ade9fbebc27d, 0xb1d219647ae6b31c,
|
||||
0xde469fbd99a05fe3, 0x8aec23d680043bee, 0xada72ccc20054aea,
|
||||
0xd910f7ff28069da4, 0x87aa9aff79042287, 0xa99541bf57452b28,
|
||||
0xd3fa922f2d1675f2, 0x847c9b5d7c2e09b7, 0xa59bc234db398c25,
|
||||
0xcf02b2c21207ef2f, 0x8161afb94b44f57d, 0xa1ba1ba79e1632dc,
|
||||
0xca28a291859bbf93, 0xfcb2cb35e702af78, 0x9defbf01b061adab,
|
||||
0xc56baec21c7a1916, 0xf6c69a72a3989f5c, 0x9a3c2087a63f6399,
|
||||
0xc0cb28a98fcf3c80, 0xf0fdf2d3f3c30b9f, 0x969eb7c47859e744,
|
||||
0xbc4665b596706115, 0xeb57ff22fc0c795a, 0x9316ff75dd87cbd8,
|
||||
0xb7dcbf5354e9bece, 0xe5d3ef282a242e82, 0x8fa475791a569d11,
|
||||
0xb38d92d760ec4455, 0xe070f78d3927556b, 0x8c469ab843b89563,
|
||||
0xaf58416654a6babb, 0xdb2e51bfe9d0696a, 0x88fcf317f22241e2,
|
||||
0xab3c2fddeeaad25b, 0xd60b3bd56a5586f2, 0x85c7056562757457,
|
||||
0xa738c6bebb12d16d, 0xd106f86e69d785c8, 0x82a45b450226b39d,
|
||||
0xa34d721642b06084, 0xcc20ce9bd35c78a5, 0xff290242c83396ce,
|
||||
0x9f79a169bd203e41, 0xc75809c42c684dd1, 0xf92e0c3537826146,
|
||||
0x9bbcc7a142b17ccc, 0xc2abf989935ddbfe, 0xf356f7ebf83552fe,
|
||||
0x98165af37b2153df, 0xbe1bf1b059e9a8d6, 0xeda2ee1c7064130c,
|
||||
0x9485d4d1c63e8be8, 0xb9a74a0637ce2ee1, 0xe8111c87c5c1ba9a,
|
||||
0x910ab1d4db9914a0, 0xb54d5e4a127f59c8, 0xe2a0b5dc971f303a,
|
||||
0x8da471a9de737e24, 0xb10d8e1456105dad, 0xdd50f1996b947519,
|
||||
0x8a5296ffe33cc930, 0xace73cbfdc0bfb7b, 0xd8210befd30efa5a,
|
||||
0x8714a775e3e95c78, 0xa8d9d1535ce3b396, 0xd31045a8341ca07c,
|
||||
0x83ea2b892091e44e, 0xa4e4b66b68b65d61, 0xce1de40642e3f4b9,
|
||||
0x80d2ae83e9ce78f4, 0xa1075a24e4421731, 0xc94930ae1d529cfd,
|
||||
0xfb9b7cd9a4a7443c, 0x9d412e0806e88aa6, 0xc491798a08a2ad4f,
|
||||
0xf5b5d7ec8acb58a3, 0x9991a6f3d6bf1766, 0xbff610b0cc6edd3f,
|
||||
0xeff394dcff8a948f, 0x95f83d0a1fb69cd9, 0xbb764c4ca7a44410,
|
||||
0xea53df5fd18d5514, 0x92746b9be2f8552c, 0xb7118682dbb66a77,
|
||||
0xe4d5e82392a40515, 0x8f05b1163ba6832d, 0xb2c71d5bca9023f8,
|
||||
0xdf78e4b2bd342cf7, 0x8bab8eefb6409c1a, 0xae9672aba3d0c321,
|
||||
0xda3c0f568cc4f3e9, 0x8865899617fb1871, 0xaa7eebfb9df9de8e,
|
||||
0xd51ea6fa85785631, 0x8533285c936b35df, 0xa67ff273b8460357,
|
||||
0xd01fef10a657842c, 0x8213f56a67f6b29c, 0xa298f2c501f45f43,
|
||||
0xcb3f2f7642717713, 0xfe0efb53d30dd4d8, 0x9ec95d1463e8a507,
|
||||
0xc67bb4597ce2ce49, 0xf81aa16fdc1b81db, 0x9b10a4e5e9913129,
|
||||
0xc1d4ce1f63f57d73, 0xf24a01a73cf2dcd0, 0x976e41088617ca02,
|
||||
0xbd49d14aa79dbc82, 0xec9c459d51852ba3, 0x93e1ab8252f33b46,
|
||||
0xb8da1662e7b00a17, 0xe7109bfba19c0c9d, 0x906a617d450187e2,
|
||||
0xb484f9dc9641e9db, 0xe1a63853bbd26451, 0x8d07e33455637eb3,
|
||||
0xb049dc016abc5e60, 0xdc5c5301c56b75f7, 0x89b9b3e11b6329bb,
|
||||
0xac2820d9623bf429, 0xd732290fbacaf134, 0x867f59a9d4bed6c0,
|
||||
0xa81f301449ee8c70, 0xd226fc195c6a2f8c, 0x83585d8fd9c25db8,
|
||||
0xa42e74f3d032f526, 0xcd3a1230c43fb26f, 0x80444b5e7aa7cf85,
|
||||
0xa0555e361951c367, 0xc86ab5c39fa63441, 0xfa856334878fc151,
|
||||
0x9c935e00d4b9d8d2, 0xc3b8358109e84f07, 0xf4a642e14c6262c9,
|
||||
0x98e7e9cccfbd7dbe, 0xbf21e44003acdd2d, 0xeeea5d5004981478,
|
||||
0x95527a5202df0ccb, 0xbaa718e68396cffe, 0xe950df20247c83fd,
|
||||
0x91d28b7416cdd27e, 0xb6472e511c81471e, 0xe3d8f9e563a198e5,
|
||||
0x8e679c2f5e44ff8f, 0xb201833b35d63f73, 0xde81e40a034bcf50,
|
||||
0x8b112e86420f6192, 0xadd57a27d29339f6, 0xd94ad8b1c7380874,
|
||||
0x87cec76f1c830549, 0xa9c2794ae3a3c69b, 0xd433179d9c8cb841,
|
||||
0x849feec281d7f329, 0xa5c7ea73224deff3, 0xcf39e50feae16bf0,
|
||||
0x81842f29f2cce376, 0xa1e53af46f801c53, 0xca5e89b18b602368,
|
||||
0xfcf62c1dee382c42, 0x9e19db92b4e31ba9, 0xc5a05277621be294,
|
||||
0xf70867153aa2db39, 0x9a65406d44a5c903, 0xc0fe908895cf3b44,
|
||||
0xf13e34aabb430a15, 0x96c6e0eab509e64d, 0xbc789925624c5fe1,
|
||||
0xeb96bf6ebadf77d9, 0x933e37a534cbaae8, 0xb80dc58e81fe95a1,
|
||||
0xe61136f2227e3b0a, 0x8fcac257558ee4e6, 0xb3bd72ed2af29e20,
|
||||
0xe0accfa875af45a8, 0x8c6c01c9498d8b89, 0xaf87023b9bf0ee6b,
|
||||
0xdb68c2ca82ed2a06, 0x892179be91d43a44, 0xab69d82e364948d4
|
||||
};
|
||||
|
||||
static const int powers_ten_e[] = {
|
||||
-1203, -1200, -1196, -1193, -1190, -1186, -1183, -1180, -1176, -1173,
|
||||
-1170, -1166, -1163, -1160, -1156, -1153, -1150, -1146, -1143, -1140,
|
||||
-1136, -1133, -1130, -1127, -1123, -1120, -1117, -1113, -1110, -1107,
|
||||
-1103, -1100, -1097, -1093, -1090, -1087, -1083, -1080, -1077, -1073,
|
||||
-1070, -1067, -1063, -1060, -1057, -1053, -1050, -1047, -1043, -1040,
|
||||
-1037, -1034, -1030, -1027, -1024, -1020, -1017, -1014, -1010, -1007,
|
||||
-1004, -1000, -997, -994, -990, -987, -984, -980, -977, -974, -970,
|
||||
-967, -964, -960, -957, -954, -950, -947, -944, -940, -937, -934, -931,
|
||||
-927, -924, -921, -917, -914, -911, -907, -904, -901, -897, -894, -891,
|
||||
-887, -884, -881, -877, -874, -871, -867, -864, -861, -857, -854, -851,
|
||||
-847, -844, -841, -838, -834, -831, -828, -824, -821, -818, -814, -811,
|
||||
-808, -804, -801, -798, -794, -791, -788, -784, -781, -778, -774, -771,
|
||||
-768, -764, -761, -758, -754, -751, -748, -744, -741, -738, -735, -731,
|
||||
-728, -725, -721, -718, -715, -711, -708, -705, -701, -698, -695, -691,
|
||||
-688, -685, -681, -678, -675, -671, -668, -665, -661, -658, -655, -651,
|
||||
-648, -645, -642, -638, -635, -632, -628, -625, -622, -618, -615, -612,
|
||||
-608, -605, -602, -598, -595, -592, -588, -585, -582, -578, -575, -572,
|
||||
-568, -565, -562, -558, -555, -552, -549, -545, -542, -539, -535, -532,
|
||||
-529, -525, -522, -519, -515, -512, -509, -505, -502, -499, -495, -492,
|
||||
-489, -485, -482, -479, -475, -472, -469, -465, -462, -459, -455, -452,
|
||||
-449, -446, -442, -439, -436, -432, -429, -426, -422, -419, -416, -412,
|
||||
-409, -406, -402, -399, -396, -392, -389, -386, -382, -379, -376, -372,
|
||||
-369, -366, -362, -359, -356, -353, -349, -346, -343, -339, -336, -333,
|
||||
-329, -326, -323, -319, -316, -313, -309, -306, -303, -299, -296, -293,
|
||||
-289, -286, -283, -279, -276, -273, -269, -266, -263, -259, -256, -253,
|
||||
-250, -246, -243, -240, -236, -233, -230, -226, -223, -220, -216, -213,
|
||||
-210, -206, -203, -200, -196, -193, -190, -186, -183, -180, -176, -173,
|
||||
-170, -166, -163, -160, -157, -153, -150, -147, -143, -140, -137, -133,
|
||||
-130, -127, -123, -120, -117, -113, -110, -107, -103, -100, -97, -93,
|
||||
-90, -87, -83, -80, -77, -73, -70, -67, -63, -60, -57, -54, -50, -47,
|
||||
-44, -40, -37, -34, -30, -27, -24, -20, -17, -14, -10, -7, -4, 0, 3, 6,
|
||||
10, 13, 16, 20, 23, 26, 30, 33, 36, 39, 43, 46, 49, 53, 56, 59, 63, 66,
|
||||
69, 73, 76, 79, 83, 86, 89, 93, 96, 99, 103, 106, 109, 113, 116, 119,
|
||||
123, 126, 129, 132, 136, 139, 142, 146, 149, 152, 156, 159, 162, 166,
|
||||
169, 172, 176, 179, 182, 186, 189, 192, 196, 199, 202, 206, 209, 212,
|
||||
216, 219, 222, 226, 229, 232, 235, 239, 242, 245, 249, 252, 255, 259,
|
||||
262, 265, 269, 272, 275, 279, 282, 285, 289, 292, 295, 299, 302, 305,
|
||||
309, 312, 315, 319, 322, 325, 328, 332, 335, 338, 342, 345, 348, 352,
|
||||
355, 358, 362, 365, 368, 372, 375, 378, 382, 385, 388, 392, 395, 398,
|
||||
402, 405, 408, 412, 415, 418, 422, 425, 428, 431, 435, 438, 441, 445,
|
||||
448, 451, 455, 458, 461, 465, 468, 471, 475, 478, 481, 485, 488, 491,
|
||||
495, 498, 501, 505, 508, 511, 515, 518, 521, 524, 528, 531, 534, 538,
|
||||
541, 544, 548, 551, 554, 558, 561, 564, 568, 571, 574, 578, 581, 584,
|
||||
588, 591, 594, 598, 601, 604, 608, 611, 614, 617, 621, 624, 627, 631,
|
||||
634, 637, 641, 644, 647, 651, 654, 657, 661, 664, 667, 671, 674, 677,
|
||||
681, 684, 687, 691, 694, 697, 701, 704, 707, 711, 714, 717, 720, 724,
|
||||
727, 730, 734, 737, 740, 744, 747, 750, 754, 757, 760, 764, 767, 770,
|
||||
774, 777, 780, 784, 787, 790, 794, 797, 800, 804, 807, 810, 813, 817,
|
||||
820, 823, 827, 830, 833, 837, 840, 843, 847, 850, 853, 857, 860, 863,
|
||||
867, 870, 873, 877, 880, 883, 887, 890, 893, 897, 900, 903, 907, 910,
|
||||
913, 916, 920, 923, 926, 930, 933, 936, 940, 943, 946, 950, 953, 956,
|
||||
960, 963, 966, 970, 973, 976, 980, 983, 986, 990, 993, 996, 1000, 1003,
|
||||
1006, 1009, 1013, 1016, 1019, 1023, 1026, 1029, 1033, 1036, 1039, 1043,
|
||||
1046, 1049, 1053, 1056, 1059, 1063, 1066, 1069, 1073, 1076
|
||||
};
|
||||
|
||||
static diy_fp_t cached_power(int k)
|
||||
{
|
||||
diy_fp_t res;
|
||||
int index = 343 + k;
|
||||
res.f = powers_ten[index];
|
||||
res.e = powers_ten_e[index];
|
||||
return res;
|
||||
}
|
||||
|
||||
static int k_comp(int e, int alpha, int gamma) {
|
||||
return ceil((alpha-e+63) * D_1_LOG2_10);
|
||||
}
|
||||
|
||||
static diy_fp_t minus(diy_fp_t x, diy_fp_t y)
|
||||
{
|
||||
diy_fp_t r;
|
||||
assert(x.e == y.e);
|
||||
assert(x.f >= y.f);
|
||||
r.f = x.f - y.f;
|
||||
r.e = x.e;
|
||||
return r;
|
||||
}
|
||||
|
||||
static diy_fp_t multiply(diy_fp_t x, diy_fp_t y)
|
||||
{
|
||||
uint64_t a,b,c,d,ac,bc,ad,bd,tmp;
|
||||
diy_fp_t r;
|
||||
uint64_t M32 = 0xFFFFFFFF;
|
||||
a = x.f >> 32; b = x.f & M32;
|
||||
c = y.f >> 32; d = y.f & M32;
|
||||
ac = a*c; bc = b*c; ad = a*d; bd = b*d;
|
||||
tmp = (bd>>32) + (ad&M32) + (bc&M32);
|
||||
tmp += 1U << 31;
|
||||
r.f = ac+(ad>>32)+(bc>>32)+(tmp >>32);
|
||||
r.e = x.e + y.e + 64;
|
||||
return r;
|
||||
}
|
||||
|
||||
static uint64_t double_to_uint64(double d)
|
||||
{
|
||||
uint64_t n;
|
||||
memcpy(&n, &d, 8);
|
||||
return n;
|
||||
}
|
||||
|
||||
#define DP_SIGNIFICAND_SIZE 52
|
||||
#define DP_EXPONENT_BIAS (0x3FF + DP_SIGNIFICAND_SIZE)
|
||||
#define DP_MIN_EXPONENT (-DP_EXPONENT_BIAS)
|
||||
#define DP_EXPONENT_MASK 0x7FF0000000000000
|
||||
#define DP_SIGNIFICAND_MASK 0x000FFFFFFFFFFFFF
|
||||
#define DP_HIDDEN_BIT 0x0010000000000000
|
||||
|
||||
static diy_fp_t double2diy_fp(double d)
|
||||
{
|
||||
uint64_t d64 = double_to_uint64(d);
|
||||
int biased_e = (d64 & DP_EXPONENT_MASK) >> DP_SIGNIFICAND_SIZE;
|
||||
uint64_t significand = (d64 & DP_SIGNIFICAND_MASK);
|
||||
diy_fp_t res;
|
||||
if (biased_e != 0) {
|
||||
res.f = significand + DP_HIDDEN_BIT;
|
||||
res.e = biased_e - DP_EXPONENT_BIAS;
|
||||
} else {
|
||||
res.f = significand;
|
||||
res.e = DP_MIN_EXPONENT + 1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static diy_fp_t normalize_boundary(diy_fp_t in)
|
||||
{
|
||||
diy_fp_t res = in;
|
||||
/* Normalize now */
|
||||
/* the original number could have been a denormal. */
|
||||
while (! (res.f & (DP_HIDDEN_BIT << 1))) {
|
||||
res.f <<= 1;
|
||||
res.e--;
|
||||
}
|
||||
/* do the final shifts in one go. Don't forget the hidden bit (the '-1') */
|
||||
res.f <<= (DIY_SIGNIFICAND_SIZE - DP_SIGNIFICAND_SIZE - 2);
|
||||
res.e = res.e - (DIY_SIGNIFICAND_SIZE - DP_SIGNIFICAND_SIZE - 2);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void normalized_boundaries(double d, diy_fp_t* out_m_minus, diy_fp_t* out_m_plus)
|
||||
{
|
||||
diy_fp_t v = double2diy_fp(d);
|
||||
diy_fp_t pl, mi;
|
||||
int significand_is_zero = v.f == DP_HIDDEN_BIT;
|
||||
pl.f = (v.f << 1) + 1; pl.e = v.e - 1;
|
||||
pl = normalize_boundary(pl);
|
||||
if (significand_is_zero) {
|
||||
mi.f = (v.f << 2) - 1;
|
||||
mi.e = v.e - 2;
|
||||
} else {
|
||||
mi.f = (v.f << 1) - 1;
|
||||
mi.e = v.e - 1;
|
||||
}
|
||||
mi.f <<= mi.e - pl.e;
|
||||
mi.e = pl.e;
|
||||
*out_m_plus = pl;
|
||||
*out_m_minus = mi;
|
||||
}
|
||||
|
||||
#define TEN2 100
|
||||
static void digit_gen(diy_fp_t Mp, diy_fp_t delta, char* buffer, int* len, int* K)
|
||||
{
|
||||
uint32_t div, p1;
|
||||
uint64_t p2;
|
||||
int d,kappa;
|
||||
diy_fp_t one;
|
||||
one.f = ((uint64_t) 1) << -Mp.e; one.e = Mp.e;
|
||||
p1 = Mp.f >> -one.e;
|
||||
p2 = Mp.f & (one.f - 1);
|
||||
*len = 0; kappa = 3; div = TEN2;
|
||||
while (kappa > 0) {
|
||||
d = p1 / div;
|
||||
if (d || *len) buffer[(*len)++] = '0' + d;
|
||||
p1 %= div; kappa--; div /= 10;
|
||||
if ((((uint64_t)p1)<<-one.e)+p2 <= delta.f) {
|
||||
*K += kappa; return;
|
||||
}
|
||||
}
|
||||
do {
|
||||
p2 *= 10;
|
||||
d = p2 >> -one.e;
|
||||
if (d || *len) buffer[(*len)++] = '0' + d;
|
||||
p2 &= one.f - 1; kappa--; delta.f *= 10;
|
||||
} while (p2 > delta.f);
|
||||
*K += kappa;
|
||||
}
|
||||
|
||||
int
|
||||
js_grisu2(double v, char *buffer, int *K)
|
||||
{
|
||||
int length, mk;
|
||||
diy_fp_t w_m, w_p, c_mk, Wp, Wm, delta;
|
||||
int q = 64, alpha = -59, gamma = -56;
|
||||
normalized_boundaries(v, &w_m, &w_p);
|
||||
mk = k_comp(w_p.e + q, alpha, gamma);
|
||||
c_mk = cached_power(mk);
|
||||
Wp = multiply(w_p, c_mk);
|
||||
Wm = multiply(w_m, c_mk);
|
||||
Wm.f++; Wp.f--;
|
||||
delta = minus(Wp, Wm);
|
||||
*K = -mk;
|
||||
digit_gen(Wp, delta, buffer, &length, K);
|
||||
return length;
|
||||
}
|
||||
|
||||
/*
|
||||
* strtod.c
|
||||
*
|
||||
* Copyright (c) 1988-1993 The Regents of the University of California.
|
||||
* Copyright (c) 1994 Sun Microsystems, Inc.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software and its
|
||||
* documentation for any purpose and without fee is hereby granted, provided
|
||||
* that the above copyright notice appear in all copies. The University of
|
||||
* California makes no representations about the suitability of this software
|
||||
* for any purpose. It is provided "as is" without express or implied warranty.
|
||||
*/
|
||||
|
||||
/* Largest possible base 10 exponent. Any exponent larger than this will
|
||||
* already produce underflow or overflow, so there's no need to worry about
|
||||
* additional digits.
|
||||
*/
|
||||
static int maxExponent = 511;
|
||||
|
||||
/* Table giving binary powers of 10. Entry
|
||||
* is 10^2^i. Used to convert decimal
|
||||
* exponents into floating-point numbers.
|
||||
*/
|
||||
static double powersOf10[] = {
|
||||
10.,
|
||||
100.,
|
||||
1.0e4,
|
||||
1.0e8,
|
||||
1.0e16,
|
||||
1.0e32,
|
||||
1.0e64,
|
||||
1.0e128,
|
||||
1.0e256
|
||||
};
|
||||
|
||||
/* Parse a decimal ASCII floating-point number, optionally preceded by white
|
||||
* space. Must have form "-I.FE-X", where I is the integer part of the
|
||||
* mantissa, F is the fractional part of the mantissa, and X is the exponent.
|
||||
* Either of the signs may be "+", "-", or omitted. Either I or F may be
|
||||
* omitted, or both. The decimal point isn't necessary unless F is present.
|
||||
* The "E" may actually be an "e". E and X may both be omitted (but not just
|
||||
* one).
|
||||
*/
|
||||
double
|
||||
js_strtod(const char *string, char **endPtr)
|
||||
{
|
||||
int sign, expSign = FALSE;
|
||||
double fraction, dblExp, *d;
|
||||
register const char *p;
|
||||
register int c;
|
||||
|
||||
/* Exponent read from "EX" field. */
|
||||
int exp = 0;
|
||||
|
||||
/* Exponent that derives from the fractional part. Under normal
|
||||
* circumstances, it is the negative of the number of digits in F.
|
||||
* However, if I is very long, the last digits of I get dropped
|
||||
* (otherwise a long I with a large negative exponent could cause an
|
||||
* unnecessary overflow on I alone). In this case, fracExp is
|
||||
* incremented one for each dropped digit.
|
||||
*/
|
||||
int fracExp = 0;
|
||||
|
||||
/* Number of digits in mantissa. */
|
||||
int mantSize;
|
||||
|
||||
/* Number of mantissa digits BEFORE decimal point. */
|
||||
int decPt;
|
||||
|
||||
/* Temporarily holds location of exponent in string. */
|
||||
const char *pExp;
|
||||
|
||||
/*
|
||||
* Strip off leading blanks and check for a sign.
|
||||
*/
|
||||
|
||||
p = string;
|
||||
while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') {
|
||||
p += 1;
|
||||
}
|
||||
if (*p == '-') {
|
||||
sign = TRUE;
|
||||
p += 1;
|
||||
} else {
|
||||
if (*p == '+') {
|
||||
p += 1;
|
||||
}
|
||||
sign = FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Count the number of digits in the mantissa (including the decimal
|
||||
* point), and also locate the decimal point.
|
||||
*/
|
||||
|
||||
decPt = -1;
|
||||
for (mantSize = 0; ; mantSize += 1)
|
||||
{
|
||||
c = *p;
|
||||
if (!(c>='0'&&c<='9')) {
|
||||
if ((c != '.') || (decPt >= 0)) {
|
||||
break;
|
||||
}
|
||||
decPt = mantSize;
|
||||
}
|
||||
p += 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now suck up the digits in the mantissa. Use two integers to
|
||||
* collect 9 digits each (this is faster than using floating-point).
|
||||
* If the mantissa has more than 18 digits, ignore the extras, since
|
||||
* they can't affect the value anyway.
|
||||
*/
|
||||
|
||||
pExp = p;
|
||||
p -= mantSize;
|
||||
if (decPt < 0) {
|
||||
decPt = mantSize;
|
||||
} else {
|
||||
mantSize -= 1; /* One of the digits was the point. */
|
||||
}
|
||||
if (mantSize > 18) {
|
||||
fracExp = decPt - 18;
|
||||
mantSize = 18;
|
||||
} else {
|
||||
fracExp = decPt - mantSize;
|
||||
}
|
||||
if (mantSize == 0) {
|
||||
fraction = 0.0;
|
||||
p = string;
|
||||
goto done;
|
||||
} else {
|
||||
int frac1, frac2;
|
||||
frac1 = 0;
|
||||
for ( ; mantSize > 9; mantSize -= 1)
|
||||
{
|
||||
c = *p;
|
||||
p += 1;
|
||||
if (c == '.') {
|
||||
c = *p;
|
||||
p += 1;
|
||||
}
|
||||
frac1 = 10*frac1 + (c - '0');
|
||||
}
|
||||
frac2 = 0;
|
||||
for (; mantSize > 0; mantSize -= 1)
|
||||
{
|
||||
c = *p;
|
||||
p += 1;
|
||||
if (c == '.') {
|
||||
c = *p;
|
||||
p += 1;
|
||||
}
|
||||
frac2 = 10*frac2 + (c - '0');
|
||||
}
|
||||
fraction = (1.0e9 * frac1) + frac2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Skim off the exponent.
|
||||
*/
|
||||
|
||||
p = pExp;
|
||||
if ((*p == 'E') || (*p == 'e')) {
|
||||
p += 1;
|
||||
if (*p == '-') {
|
||||
expSign = TRUE;
|
||||
p += 1;
|
||||
} else {
|
||||
if (*p == '+') {
|
||||
p += 1;
|
||||
}
|
||||
expSign = FALSE;
|
||||
}
|
||||
while ((*p >= '0') && (*p <= '9') && exp < INT_MAX/100) {
|
||||
exp = exp * 10 + (*p - '0');
|
||||
p += 1;
|
||||
}
|
||||
while ((*p >= '0') && (*p <= '9'))
|
||||
p += 1;
|
||||
}
|
||||
if (expSign) {
|
||||
exp = fracExp - exp;
|
||||
} else {
|
||||
exp = fracExp + exp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate a floating-point number that represents the exponent.
|
||||
* Do this by processing the exponent one bit at a time to combine
|
||||
* many powers of 2 of 10. Then combine the exponent with the
|
||||
* fraction.
|
||||
*/
|
||||
|
||||
if (exp < -maxExponent) {
|
||||
exp = maxExponent;
|
||||
expSign = TRUE;
|
||||
errno = ERANGE;
|
||||
} else if (exp > maxExponent) {
|
||||
exp = maxExponent;
|
||||
expSign = FALSE;
|
||||
errno = ERANGE;
|
||||
} else if (exp < 0) {
|
||||
expSign = TRUE;
|
||||
exp = -exp;
|
||||
} else {
|
||||
expSign = FALSE;
|
||||
}
|
||||
dblExp = 1.0;
|
||||
for (d = powersOf10; exp != 0; exp >>= 1, d += 1) {
|
||||
if (exp & 01) {
|
||||
dblExp *= *d;
|
||||
}
|
||||
}
|
||||
if (expSign) {
|
||||
fraction /= dblExp;
|
||||
} else {
|
||||
fraction *= dblExp;
|
||||
}
|
||||
|
||||
done:
|
||||
if (endPtr != NULL) {
|
||||
*endPtr = (char *) p;
|
||||
}
|
||||
|
||||
if (sign) {
|
||||
return -fraction;
|
||||
}
|
||||
return fraction;
|
||||
}
|
128
src/mujs/jserror.c
Normal file
128
src/mujs/jserror.c
Normal file
|
@ -0,0 +1,128 @@
|
|||
#include "jsi.h"
|
||||
|
||||
#define QQ(X) #X
|
||||
#define Q(X) QQ(X)
|
||||
|
||||
static int jsB_stacktrace(js_State *J, int skip)
|
||||
{
|
||||
char buf[256];
|
||||
int n = J->tracetop - skip;
|
||||
if (n <= 0)
|
||||
return 0;
|
||||
for (; n > 0; --n) {
|
||||
const char *name = J->trace[n].name;
|
||||
const char *file = J->trace[n].file;
|
||||
int line = J->trace[n].line;
|
||||
if (line > 0) {
|
||||
if (name[0])
|
||||
npf_snprintf(buf, sizeof buf, "\n\tat %s (%s:%d)", name, file, line);
|
||||
else
|
||||
npf_snprintf(buf, sizeof buf, "\n\tat %s:%d", file, line);
|
||||
} else
|
||||
npf_snprintf(buf, sizeof buf, "\n\tat %s (%s)", name, file);
|
||||
js_pushstring(J, buf);
|
||||
if (n < J->tracetop - skip)
|
||||
js_concat(J);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void Ep_toString(js_State *J)
|
||||
{
|
||||
const char *name = "Error";
|
||||
const char *message = "";
|
||||
|
||||
if (!js_isobject(J, -1))
|
||||
js_typeerror(J, "not an object");
|
||||
|
||||
if (js_hasproperty(J, 0, "name"))
|
||||
name = js_tostring(J, -1);
|
||||
if (js_hasproperty(J, 0, "message"))
|
||||
message = js_tostring(J, -1);
|
||||
|
||||
if (name[0] == 0)
|
||||
js_pushstring(J, message);
|
||||
else if (message[0] == 0)
|
||||
js_pushstring(J, name);
|
||||
else {
|
||||
js_pushstring(J, name);
|
||||
js_pushstring(J, ": ");
|
||||
js_concat(J);
|
||||
js_pushstring(J, message);
|
||||
js_concat(J);
|
||||
}
|
||||
}
|
||||
|
||||
static int jsB_ErrorX(js_State *J, js_Object *prototype)
|
||||
{
|
||||
js_pushobject(J, jsV_newobject(J, JS_CERROR, prototype));
|
||||
if (js_isdefined(J, 1)) {
|
||||
js_pushstring(J, js_tostring(J, 1));
|
||||
js_defproperty(J, -2, "message", JS_DONTENUM);
|
||||
}
|
||||
if (jsB_stacktrace(J, 1))
|
||||
js_defproperty(J, -2, "stackTrace", JS_DONTENUM);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void js_newerrorx(js_State *J, const char *message, js_Object *prototype)
|
||||
{
|
||||
js_pushobject(J, jsV_newobject(J, JS_CERROR, prototype));
|
||||
js_pushstring(J, message);
|
||||
js_setproperty(J, -2, "message");
|
||||
if (jsB_stacktrace(J, 0))
|
||||
js_setproperty(J, -2, "stackTrace");
|
||||
}
|
||||
|
||||
#define DERROR(name, Name) \
|
||||
static void jsB_##Name(js_State *J) { \
|
||||
jsB_ErrorX(J, J->Name##_prototype); \
|
||||
} \
|
||||
void js_new##name(js_State *J, const char *s) { \
|
||||
js_newerrorx(J, s, J->Name##_prototype); \
|
||||
} \
|
||||
void js_##name(js_State *J, const char *fmt, ...) { \
|
||||
va_list ap; \
|
||||
char buf[256]; \
|
||||
va_start(ap, fmt); \
|
||||
npf_vsnprintf(buf, sizeof buf, fmt, ap); \
|
||||
va_end(ap); \
|
||||
js_newerrorx(J, buf, J->Name##_prototype); \
|
||||
js_throw(J); \
|
||||
}
|
||||
|
||||
DERROR(error, Error)
|
||||
DERROR(evalerror, EvalError)
|
||||
DERROR(rangeerror, RangeError)
|
||||
DERROR(referenceerror, ReferenceError)
|
||||
DERROR(syntaxerror, SyntaxError)
|
||||
DERROR(typeerror, TypeError)
|
||||
DERROR(urierror, URIError)
|
||||
|
||||
#undef DERROR
|
||||
|
||||
void jsB_initerror(js_State *J)
|
||||
{
|
||||
js_pushobject(J, J->Error_prototype);
|
||||
{
|
||||
jsB_props(J, "name", "Error");
|
||||
jsB_propf(J, "Error.prototype.toString", Ep_toString, 0);
|
||||
}
|
||||
js_newcconstructor(J, jsB_Error, jsB_Error, "Error", 1);
|
||||
js_defglobal(J, "Error", JS_DONTENUM);
|
||||
|
||||
#define IERROR(NAME) \
|
||||
js_pushobject(J, J->NAME##_prototype); \
|
||||
jsB_props(J, "name", Q(NAME)); \
|
||||
js_newcconstructor(J, jsB_##NAME, jsB_##NAME, Q(NAME), 1); \
|
||||
js_defglobal(J, Q(NAME), JS_DONTENUM);
|
||||
|
||||
IERROR(EvalError);
|
||||
IERROR(RangeError);
|
||||
IERROR(ReferenceError);
|
||||
IERROR(SyntaxError);
|
||||
IERROR(TypeError);
|
||||
IERROR(URIError);
|
||||
|
||||
#undef IERROR
|
||||
}
|
231
src/mujs/jsfunction.c
Normal file
231
src/mujs/jsfunction.c
Normal file
|
@ -0,0 +1,231 @@
|
|||
#include "jsi.h"
|
||||
|
||||
static void jsB_Function(js_State *J)
|
||||
{
|
||||
int i, top = js_gettop(J);
|
||||
js_Buffer *sb = NULL;
|
||||
const char *body;
|
||||
js_Ast *parse;
|
||||
js_Function *fun;
|
||||
|
||||
if (js_try(J)) {
|
||||
js_free(J, sb);
|
||||
jsP_freeparse(J);
|
||||
js_throw(J);
|
||||
}
|
||||
|
||||
/* p1, p2, ..., pn */
|
||||
if (top > 2) {
|
||||
for (i = 1; i < top - 1; ++i) {
|
||||
if (i > 1)
|
||||
js_putc(J, &sb, ',');
|
||||
js_puts(J, &sb, js_tostring(J, i));
|
||||
}
|
||||
js_putc(J, &sb, ')');
|
||||
js_putc(J, &sb, 0);
|
||||
}
|
||||
|
||||
/* body */
|
||||
body = js_isdefined(J, top - 1) ? js_tostring(J, top - 1) : "";
|
||||
|
||||
parse = jsP_parsefunction(J, "[string]", sb ? sb->s : NULL, body);
|
||||
fun = jsC_compilefunction(J, parse);
|
||||
|
||||
js_endtry(J);
|
||||
js_free(J, sb);
|
||||
jsP_freeparse(J);
|
||||
|
||||
js_newfunction(J, fun, J->GE);
|
||||
}
|
||||
|
||||
static void jsB_Function_prototype(js_State *J)
|
||||
{
|
||||
js_pushundefined(J);
|
||||
}
|
||||
|
||||
static void Fp_toString(js_State *J)
|
||||
{
|
||||
js_Object *self = js_toobject(J, 0);
|
||||
js_Buffer *sb = NULL;
|
||||
int i;
|
||||
|
||||
if (!js_iscallable(J, 0))
|
||||
js_typeerror(J, "not a function");
|
||||
|
||||
if (self->type == JS_CFUNCTION || self->type == JS_CSCRIPT) {
|
||||
js_Function *F = self->u.f.function;
|
||||
|
||||
if (js_try(J)) {
|
||||
js_free(J, sb);
|
||||
js_throw(J);
|
||||
}
|
||||
|
||||
js_puts(J, &sb, "function ");
|
||||
js_puts(J, &sb, F->name);
|
||||
js_putc(J, &sb, '(');
|
||||
for (i = 0; i < F->numparams; ++i) {
|
||||
if (i > 0) js_putc(J, &sb, ',');
|
||||
js_puts(J, &sb, F->vartab[i]);
|
||||
}
|
||||
js_puts(J, &sb, ") { [byte code] }");
|
||||
js_putc(J, &sb, 0);
|
||||
|
||||
js_pushstring(J, sb->s);
|
||||
js_endtry(J);
|
||||
js_free(J, sb);
|
||||
} else if (self->type == JS_CCFUNCTION) {
|
||||
if (js_try(J)) {
|
||||
js_free(J, sb);
|
||||
js_throw(J);
|
||||
}
|
||||
|
||||
js_puts(J, &sb, "function ");
|
||||
js_puts(J, &sb, self->u.c.name);
|
||||
js_puts(J, &sb, "() { [native code] }");
|
||||
js_putc(J, &sb, 0);
|
||||
|
||||
js_pushstring(J, sb->s);
|
||||
js_endtry(J);
|
||||
js_free(J, sb);
|
||||
} else {
|
||||
js_pushliteral(J, "function () { }");
|
||||
}
|
||||
}
|
||||
|
||||
static void Fp_apply(js_State *J)
|
||||
{
|
||||
int i, n;
|
||||
|
||||
if (!js_iscallable(J, 0))
|
||||
js_typeerror(J, "not a function");
|
||||
|
||||
js_copy(J, 0);
|
||||
js_copy(J, 1);
|
||||
|
||||
if (js_isnull(J, 2) || js_isundefined(J, 2)) {
|
||||
n = 0;
|
||||
} else {
|
||||
n = js_getlength(J, 2);
|
||||
if (n < 0)
|
||||
n = 0;
|
||||
for (i = 0; i < n; ++i)
|
||||
js_getindex(J, 2, i);
|
||||
}
|
||||
|
||||
js_call(J, n);
|
||||
}
|
||||
|
||||
static void Fp_call(js_State *J)
|
||||
{
|
||||
int i, top = js_gettop(J);
|
||||
|
||||
if (!js_iscallable(J, 0))
|
||||
js_typeerror(J, "not a function");
|
||||
|
||||
for (i = 0; i < top; ++i)
|
||||
js_copy(J, i);
|
||||
|
||||
js_call(J, top - 2);
|
||||
}
|
||||
|
||||
static void callbound(js_State *J)
|
||||
{
|
||||
int top = js_gettop(J);
|
||||
int i, fun, args, n;
|
||||
|
||||
fun = js_gettop(J);
|
||||
js_currentfunction(J);
|
||||
js_getproperty(J, fun, "__TargetFunction__");
|
||||
js_getproperty(J, fun, "__BoundThis__");
|
||||
|
||||
args = js_gettop(J);
|
||||
js_getproperty(J, fun, "__BoundArguments__");
|
||||
n = js_getlength(J, args);
|
||||
if (n < 0)
|
||||
n = 0;
|
||||
for (i = 0; i < n; ++i)
|
||||
js_getindex(J, args, i);
|
||||
js_remove(J, args);
|
||||
|
||||
for (i = 1; i < top; ++i)
|
||||
js_copy(J, i);
|
||||
|
||||
js_call(J, n + top - 1);
|
||||
}
|
||||
|
||||
static void constructbound(js_State *J)
|
||||
{
|
||||
int top = js_gettop(J);
|
||||
int i, fun, args, n;
|
||||
|
||||
fun = js_gettop(J);
|
||||
js_currentfunction(J);
|
||||
js_getproperty(J, fun, "__TargetFunction__");
|
||||
|
||||
args = js_gettop(J);
|
||||
js_getproperty(J, fun, "__BoundArguments__");
|
||||
n = js_getlength(J, args);
|
||||
if (n < 0)
|
||||
n = 0;
|
||||
for (i = 0; i < n; ++i)
|
||||
js_getindex(J, args, i);
|
||||
js_remove(J, args);
|
||||
|
||||
for (i = 1; i < top; ++i)
|
||||
js_copy(J, i);
|
||||
|
||||
js_construct(J, n + top - 1);
|
||||
}
|
||||
|
||||
static void Fp_bind(js_State *J)
|
||||
{
|
||||
int i, top = js_gettop(J);
|
||||
int n;
|
||||
|
||||
if (!js_iscallable(J, 0))
|
||||
js_typeerror(J, "not a function");
|
||||
|
||||
n = js_getlength(J, 0);
|
||||
if (n > top - 2)
|
||||
n -= top - 2;
|
||||
else
|
||||
n = 0;
|
||||
|
||||
/* Reuse target function's prototype for HasInstance check. */
|
||||
js_getproperty(J, 0, "prototype");
|
||||
js_newcconstructor(J, callbound, constructbound, "[bind]", n);
|
||||
|
||||
/* target function */
|
||||
js_copy(J, 0);
|
||||
js_defproperty(J, -2, "__TargetFunction__", JS_READONLY | JS_DONTENUM | JS_DONTCONF);
|
||||
|
||||
/* bound this */
|
||||
js_copy(J, 1);
|
||||
js_defproperty(J, -2, "__BoundThis__", JS_READONLY | JS_DONTENUM | JS_DONTCONF);
|
||||
|
||||
/* bound arguments */
|
||||
js_newarray(J);
|
||||
for (i = 2; i < top; ++i) {
|
||||
js_copy(J, i);
|
||||
js_setindex(J, -2, i - 2);
|
||||
}
|
||||
js_defproperty(J, -2, "__BoundArguments__", JS_READONLY | JS_DONTENUM | JS_DONTCONF);
|
||||
}
|
||||
|
||||
void jsB_initfunction(js_State *J)
|
||||
{
|
||||
J->Function_prototype->u.c.name = "Function.prototype";
|
||||
J->Function_prototype->u.c.function = jsB_Function_prototype;
|
||||
J->Function_prototype->u.c.constructor = NULL;
|
||||
J->Function_prototype->u.c.length = 0;
|
||||
|
||||
js_pushobject(J, J->Function_prototype);
|
||||
{
|
||||
jsB_propf(J, "Function.prototype.toString", Fp_toString, 2);
|
||||
jsB_propf(J, "Function.prototype.apply", Fp_apply, 2);
|
||||
jsB_propf(J, "Function.prototype.call", Fp_call, 1);
|
||||
jsB_propf(J, "Function.prototype.bind", Fp_bind, 1);
|
||||
}
|
||||
js_newcconstructor(J, jsB_Function, jsB_Function, "Function", 1);
|
||||
js_defglobal(J, "Function", JS_DONTENUM);
|
||||
}
|
284
src/mujs/jsgc.c
Normal file
284
src/mujs/jsgc.c
Normal file
|
@ -0,0 +1,284 @@
|
|||
#include "jsi.h"
|
||||
#include "regexp.h"
|
||||
|
||||
static void jsG_freeenvironment(js_State *J, js_Environment *env)
|
||||
{
|
||||
js_free(J, env);
|
||||
}
|
||||
|
||||
static void jsG_freefunction(js_State *J, js_Function *fun)
|
||||
{
|
||||
js_free(J, fun->funtab);
|
||||
js_free(J, fun->vartab);
|
||||
js_free(J, fun->code);
|
||||
js_free(J, fun);
|
||||
}
|
||||
|
||||
static void jsG_freeproperty(js_State *J, js_Property *node)
|
||||
{
|
||||
if (node->left->level) jsG_freeproperty(J, node->left);
|
||||
if (node->right->level) jsG_freeproperty(J, node->right);
|
||||
js_free(J, node);
|
||||
}
|
||||
|
||||
static void jsG_freeiterator(js_State *J, js_Iterator *node)
|
||||
{
|
||||
while (node) {
|
||||
js_Iterator *next = node->next;
|
||||
js_free(J, node);
|
||||
node = next;
|
||||
}
|
||||
}
|
||||
|
||||
static void jsG_freeobject(js_State *J, js_Object *obj)
|
||||
{
|
||||
if (obj->properties->level)
|
||||
jsG_freeproperty(J, obj->properties);
|
||||
if (obj->type == JS_CREGEXP) {
|
||||
js_free(J, obj->u.r.source);
|
||||
js_regfreex(J->alloc, J->actx, obj->u.r.prog);
|
||||
}
|
||||
if (obj->type == JS_CSTRING) {
|
||||
if (obj->u.s.string != obj->u.s.shrstr)
|
||||
js_free(J, obj->u.s.string);
|
||||
}
|
||||
if (obj->type == JS_CARRAY && obj->u.a.simple)
|
||||
js_free(J, obj->u.a.array);
|
||||
if (obj->type == JS_CITERATOR)
|
||||
jsG_freeiterator(J, obj->u.iter.head);
|
||||
if (obj->type == JS_CUSERDATA && obj->u.user.finalize)
|
||||
obj->u.user.finalize(J, obj->u.user.data);
|
||||
if (obj->type == JS_CCFUNCTION && obj->u.c.finalize)
|
||||
obj->u.c.finalize(J, obj->u.c.data);
|
||||
js_free(J, obj);
|
||||
}
|
||||
|
||||
/* Mark and add object to scan queue */
|
||||
static void jsG_markobject(js_State *J, int mark, js_Object *obj)
|
||||
{
|
||||
obj->gcmark = mark;
|
||||
obj->gcroot = J->gcroot;
|
||||
J->gcroot = obj;
|
||||
}
|
||||
|
||||
static void jsG_markfunction(js_State *J, int mark, js_Function *fun)
|
||||
{
|
||||
int i;
|
||||
fun->gcmark = mark;
|
||||
for (i = 0; i < fun->funlen; ++i)
|
||||
if (fun->funtab[i]->gcmark != mark)
|
||||
jsG_markfunction(J, mark, fun->funtab[i]);
|
||||
}
|
||||
|
||||
static void jsG_markenvironment(js_State *J, int mark, js_Environment *env)
|
||||
{
|
||||
do {
|
||||
env->gcmark = mark;
|
||||
if (env->variables->gcmark != mark)
|
||||
jsG_markobject(J, mark, env->variables);
|
||||
env = env->outer;
|
||||
} while (env && env->gcmark != mark);
|
||||
}
|
||||
|
||||
static void jsG_markproperty(js_State *J, int mark, js_Property *node)
|
||||
{
|
||||
if (node->left->level) jsG_markproperty(J, mark, node->left);
|
||||
if (node->right->level) jsG_markproperty(J, mark, node->right);
|
||||
|
||||
if (node->value.t.type == JS_TMEMSTR && node->value.u.memstr->gcmark != mark)
|
||||
node->value.u.memstr->gcmark = mark;
|
||||
if (node->value.t.type == JS_TOBJECT && node->value.u.object->gcmark != mark)
|
||||
jsG_markobject(J, mark, node->value.u.object);
|
||||
if (node->getter && node->getter->gcmark != mark)
|
||||
jsG_markobject(J, mark, node->getter);
|
||||
if (node->setter && node->setter->gcmark != mark)
|
||||
jsG_markobject(J, mark, node->setter);
|
||||
}
|
||||
|
||||
/* Mark everything the object can reach. */
|
||||
static void jsG_scanobject(js_State *J, int mark, js_Object *obj)
|
||||
{
|
||||
if (obj->properties->level)
|
||||
jsG_markproperty(J, mark, obj->properties);
|
||||
if (obj->prototype && obj->prototype->gcmark != mark)
|
||||
jsG_markobject(J, mark, obj->prototype);
|
||||
if (obj->type == JS_CARRAY && obj->u.a.simple) {
|
||||
int i;
|
||||
for (i = 0; i < obj->u.a.flat_length; ++i) {
|
||||
js_Value *v = &obj->u.a.array[i];
|
||||
if (v->t.type == JS_TMEMSTR && v->u.memstr->gcmark != mark)
|
||||
v->u.memstr->gcmark = mark;
|
||||
if (v->t.type == JS_TOBJECT && v->u.object->gcmark != mark)
|
||||
jsG_markobject(J, mark, v->u.object);
|
||||
}
|
||||
}
|
||||
if (obj->type == JS_CITERATOR && obj->u.iter.target->gcmark != mark) {
|
||||
jsG_markobject(J, mark, obj->u.iter.target);
|
||||
}
|
||||
if (obj->type == JS_CFUNCTION || obj->type == JS_CSCRIPT) {
|
||||
if (obj->u.f.scope && obj->u.f.scope->gcmark != mark)
|
||||
jsG_markenvironment(J, mark, obj->u.f.scope);
|
||||
if (obj->u.f.function && obj->u.f.function->gcmark != mark)
|
||||
jsG_markfunction(J, mark, obj->u.f.function);
|
||||
}
|
||||
}
|
||||
|
||||
static void jsG_markstack(js_State *J, int mark)
|
||||
{
|
||||
js_Value *v = J->stack;
|
||||
int n = J->top;
|
||||
while (n--) {
|
||||
if (v->t.type == JS_TMEMSTR && v->u.memstr->gcmark != mark)
|
||||
v->u.memstr->gcmark = mark;
|
||||
if (v->t.type == JS_TOBJECT && v->u.object->gcmark != mark)
|
||||
jsG_markobject(J, mark, v->u.object);
|
||||
++v;
|
||||
}
|
||||
}
|
||||
|
||||
void js_gc(js_State *J, int report)
|
||||
{
|
||||
js_Function *fun, *nextfun, **prevnextfun;
|
||||
js_Object *obj, *nextobj, **prevnextobj;
|
||||
js_String *str, *nextstr, **prevnextstr;
|
||||
js_Environment *env, *nextenv, **prevnextenv;
|
||||
unsigned int nenv = 0, nfun = 0, nobj = 0, nstr = 0, nprop = 0;
|
||||
unsigned int genv = 0, gfun = 0, gobj = 0, gstr = 0, gprop = 0;
|
||||
int mark;
|
||||
int i;
|
||||
|
||||
mark = J->gcmark = J->gcmark == 1 ? 2 : 1;
|
||||
|
||||
/* Add initial roots. */
|
||||
|
||||
jsG_markobject(J, mark, J->Object_prototype);
|
||||
jsG_markobject(J, mark, J->Array_prototype);
|
||||
jsG_markobject(J, mark, J->Function_prototype);
|
||||
jsG_markobject(J, mark, J->Boolean_prototype);
|
||||
jsG_markobject(J, mark, J->Number_prototype);
|
||||
jsG_markobject(J, mark, J->String_prototype);
|
||||
jsG_markobject(J, mark, J->RegExp_prototype);
|
||||
jsG_markobject(J, mark, J->Date_prototype);
|
||||
|
||||
jsG_markobject(J, mark, J->Error_prototype);
|
||||
jsG_markobject(J, mark, J->EvalError_prototype);
|
||||
jsG_markobject(J, mark, J->RangeError_prototype);
|
||||
jsG_markobject(J, mark, J->ReferenceError_prototype);
|
||||
jsG_markobject(J, mark, J->SyntaxError_prototype);
|
||||
jsG_markobject(J, mark, J->TypeError_prototype);
|
||||
jsG_markobject(J, mark, J->URIError_prototype);
|
||||
|
||||
jsG_markobject(J, mark, J->R);
|
||||
jsG_markobject(J, mark, J->G);
|
||||
|
||||
jsG_markstack(J, mark);
|
||||
|
||||
jsG_markenvironment(J, mark, J->E);
|
||||
jsG_markenvironment(J, mark, J->GE);
|
||||
for (i = 0; i < J->envtop; ++i)
|
||||
jsG_markenvironment(J, mark, J->envstack[i]);
|
||||
|
||||
/* Scan objects until none remain. */
|
||||
|
||||
while ((obj = J->gcroot) != NULL) {
|
||||
J->gcroot = obj->gcroot;
|
||||
obj->gcroot = NULL;
|
||||
jsG_scanobject(J, mark, obj);
|
||||
}
|
||||
|
||||
/* Free everything not marked. */
|
||||
|
||||
prevnextenv = &J->gcenv;
|
||||
for (env = J->gcenv; env; env = nextenv) {
|
||||
nextenv = env->gcnext;
|
||||
if (env->gcmark != mark) {
|
||||
*prevnextenv = nextenv;
|
||||
jsG_freeenvironment(J, env);
|
||||
++genv;
|
||||
} else {
|
||||
prevnextenv = &env->gcnext;
|
||||
}
|
||||
++nenv;
|
||||
}
|
||||
|
||||
prevnextfun = &J->gcfun;
|
||||
for (fun = J->gcfun; fun; fun = nextfun) {
|
||||
nextfun = fun->gcnext;
|
||||
if (fun->gcmark != mark) {
|
||||
*prevnextfun = nextfun;
|
||||
jsG_freefunction(J, fun);
|
||||
++gfun;
|
||||
} else {
|
||||
prevnextfun = &fun->gcnext;
|
||||
}
|
||||
++nfun;
|
||||
}
|
||||
|
||||
prevnextobj = &J->gcobj;
|
||||
for (obj = J->gcobj; obj; obj = nextobj) {
|
||||
nprop += obj->count;
|
||||
nextobj = obj->gcnext;
|
||||
if (obj->gcmark != mark) {
|
||||
gprop += obj->count;
|
||||
*prevnextobj = nextobj;
|
||||
jsG_freeobject(J, obj);
|
||||
++gobj;
|
||||
} else {
|
||||
prevnextobj = &obj->gcnext;
|
||||
}
|
||||
++nobj;
|
||||
}
|
||||
|
||||
prevnextstr = &J->gcstr;
|
||||
for (str = J->gcstr; str; str = nextstr) {
|
||||
nextstr = str->gcnext;
|
||||
if (str->gcmark != mark) {
|
||||
*prevnextstr = nextstr;
|
||||
js_free(J, str);
|
||||
++gstr;
|
||||
} else {
|
||||
prevnextstr = &str->gcnext;
|
||||
}
|
||||
++nstr;
|
||||
}
|
||||
|
||||
unsigned int ntot = nenv + nfun + nobj + nstr + nprop;
|
||||
unsigned int gtot = genv + gfun + gobj + gstr + gprop;
|
||||
unsigned int remaining = ntot - gtot;
|
||||
|
||||
J->gccounter = remaining;
|
||||
J->gcthresh = remaining * JS_GCFACTOR;
|
||||
|
||||
if (report) {
|
||||
char buf[256];
|
||||
npf_snprintf(buf, sizeof buf, "garbage collected (%d%%): %d/%d envs, %d/%d funs, %d/%d objs, %d/%d props, %d/%d strs",
|
||||
100*gtot/ntot, genv, nenv, gfun, nfun, gobj, nobj, gprop, nprop, gstr, nstr);
|
||||
js_report(J, buf);
|
||||
}
|
||||
}
|
||||
|
||||
void js_freestate(js_State *J)
|
||||
{
|
||||
js_Function *fun, *nextfun;
|
||||
js_Object *obj, *nextobj;
|
||||
js_Environment *env, *nextenv;
|
||||
js_String *str, *nextstr;
|
||||
|
||||
if (!J)
|
||||
return;
|
||||
|
||||
for (env = J->gcenv; env; env = nextenv)
|
||||
nextenv = env->gcnext, jsG_freeenvironment(J, env);
|
||||
for (fun = J->gcfun; fun; fun = nextfun)
|
||||
nextfun = fun->gcnext, jsG_freefunction(J, fun);
|
||||
for (obj = J->gcobj; obj; obj = nextobj)
|
||||
nextobj = obj->gcnext, jsG_freeobject(J, obj);
|
||||
for (str = J->gcstr; str; str = nextstr)
|
||||
nextstr = str->gcnext, js_free(J, str);
|
||||
|
||||
jsS_freestrings(J);
|
||||
|
||||
js_free(J, J->lexbuf.text);
|
||||
J->alloc(J->actx, J->stack, 0);
|
||||
J->alloc(J->actx, J, 0);
|
||||
}
|
867
src/mujs/jsi.h
Normal file
867
src/mujs/jsi.h
Normal file
|
@ -0,0 +1,867 @@
|
|||
#ifndef jsi_h
|
||||
#define jsi_h
|
||||
|
||||
#include "mujs.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <setjmp.h>
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
#include <limits.h>
|
||||
|
||||
/* NOTE: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103052 */
|
||||
#ifdef __GNUC__
|
||||
#if (__GNUC__ >= 6)
|
||||
#pragma GCC optimize ("no-ipa-pure-const")
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Microsoft Visual C */
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4996) /* _CRT_SECURE_NO_WARNINGS */
|
||||
#pragma warning(disable:4244) /* implicit conversion from double to int */
|
||||
#pragma warning(disable:4267) /* implicit conversion of int to smaller int */
|
||||
#pragma warning(disable:4090) /* broken const warnings */
|
||||
#define inline __inline
|
||||
#if _MSC_VER < 1900 /* MSVC 2015 */
|
||||
#define snprintf jsW_snprintf
|
||||
#define vsnprintf jsW_vsnprintf
|
||||
static int jsW_vsnprintf(char *str, size_t size, const char *fmt, va_list ap)
|
||||
{
|
||||
int n;
|
||||
n = _vsnprintf(str, size, fmt, ap);
|
||||
str[size-1] = 0;
|
||||
return n;
|
||||
}
|
||||
static int jsW_snprintf(char *str, size_t size, const char *fmt, ...)
|
||||
{
|
||||
int n;
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
n = jsW_vsnprintf(str, size, fmt, ap);
|
||||
va_end(ap);
|
||||
return n;
|
||||
}
|
||||
#endif
|
||||
#if _MSC_VER <= 1700 /* <= MSVC 2012 */
|
||||
#define isnan(x) _isnan(x)
|
||||
#define isinf(x) (!_finite(x))
|
||||
#define isfinite(x) _finite(x)
|
||||
static __inline int signbit(double x) { __int64 i; memcpy(&i, &x, 8); return i>>63; }
|
||||
#define INFINITY (DBL_MAX+DBL_MAX)
|
||||
#define NAN (INFINITY-INFINITY)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define soffsetof(x,y) ((int)offsetof(x,y))
|
||||
#define nelem(a) (int)(sizeof (a) / sizeof (a)[0])
|
||||
|
||||
void *js_malloc(js_State *J, int size);
|
||||
void *js_realloc(js_State *J, void *ptr, int size);
|
||||
void js_free(js_State *J, void *ptr);
|
||||
|
||||
typedef union js_Value js_Value;
|
||||
typedef struct js_Regexp js_Regexp;
|
||||
typedef struct js_Object js_Object;
|
||||
typedef struct js_String js_String;
|
||||
typedef struct js_Ast js_Ast;
|
||||
typedef struct js_Function js_Function;
|
||||
typedef struct js_Environment js_Environment;
|
||||
typedef struct js_StringNode js_StringNode;
|
||||
typedef struct js_Jumpbuf js_Jumpbuf;
|
||||
typedef struct js_StackTrace js_StackTrace;
|
||||
|
||||
/* Limits */
|
||||
|
||||
#ifndef JS_STACKSIZE
|
||||
#define JS_STACKSIZE 4096 /* value stack size */
|
||||
#endif
|
||||
#ifndef JS_ENVLIMIT
|
||||
#define JS_ENVLIMIT 1024 /* environment stack size */
|
||||
#endif
|
||||
#ifndef JS_TRYLIMIT
|
||||
#define JS_TRYLIMIT 64 /* exception stack size */
|
||||
#endif
|
||||
|
||||
#ifndef JS_ARRAYLIMIT
|
||||
#define JS_ARRAYLIMIT (1<<26) /* limit arrays to 64M entries (1G of flat array data) */
|
||||
#endif
|
||||
|
||||
#ifndef JS_GCFACTOR
|
||||
/*
|
||||
* GC will try to trigger when memory usage is this value times the minimum
|
||||
* needed memory. E.g. if there are 100 remaining objects after GC and this
|
||||
* value is 5.0, then the next GC will trigger when the overall number is 500.
|
||||
* I.e. a value of 5.0 aims at 80% garbage, 20% remain-used on each GC.
|
||||
* The bigger the value the less impact GC has on overall performance, but more
|
||||
* memory is used and individual GC pauses are longer (but fewer).
|
||||
*/
|
||||
#define JS_GCFACTOR 5.0 /* memory overhead factor >= 1.0 */
|
||||
#endif
|
||||
|
||||
#ifndef JS_ASTLIMIT
|
||||
#define JS_ASTLIMIT 400 /* max nested expressions */
|
||||
#endif
|
||||
|
||||
#ifndef JS_STRLIMIT
|
||||
#define JS_STRLIMIT (1<<28) /* max string length */
|
||||
#endif
|
||||
|
||||
/* instruction size -- change to int if you get integer overflow syntax errors */
|
||||
|
||||
#ifdef JS_INSTRUCTION
|
||||
typedef JS_INSTRUCTION js_Instruction;
|
||||
#else
|
||||
typedef unsigned short js_Instruction;
|
||||
#endif
|
||||
|
||||
/* String interning */
|
||||
|
||||
char *js_strdup(js_State *J, const char *s);
|
||||
const char *js_intern(js_State *J, const char *s);
|
||||
void jsS_dumpstrings(js_State *J);
|
||||
void jsS_freestrings(js_State *J);
|
||||
|
||||
/* Portable strtod and printf float formatting */
|
||||
|
||||
void js_fmtexp(char *p, int e);
|
||||
int js_grisu2(double v, char *buffer, int *K);
|
||||
double js_strtod(const char *as, char **aas);
|
||||
|
||||
double js_strtol(const char *s, char **ep, int radix);
|
||||
|
||||
/* Private stack functions */
|
||||
|
||||
void js_newarguments(js_State *J);
|
||||
void js_newfunction(js_State *J, js_Function *function, js_Environment *scope);
|
||||
void js_newscript(js_State *J, js_Function *function, js_Environment *scope);
|
||||
void js_loadeval(js_State *J, const char *filename, const char *source);
|
||||
|
||||
js_Regexp *js_toregexp(js_State *J, int idx);
|
||||
int js_isarrayindex(js_State *J, const char *str, int *idx);
|
||||
int js_runeat(js_State *J, const char *s, int i);
|
||||
int js_utflen(const char *s);
|
||||
int js_utfptrtoidx(const char *s, const char *p);
|
||||
|
||||
void js_dup(js_State *J);
|
||||
void js_dup2(js_State *J);
|
||||
void js_rot2(js_State *J);
|
||||
void js_rot3(js_State *J);
|
||||
void js_rot4(js_State *J);
|
||||
void js_rot2pop1(js_State *J);
|
||||
void js_rot3pop2(js_State *J);
|
||||
void js_dup1rot3(js_State *J);
|
||||
void js_dup1rot4(js_State *J);
|
||||
|
||||
void js_RegExp_prototype_exec(js_State *J, js_Regexp *re, const char *text);
|
||||
|
||||
void js_trap(js_State *J, int pc); /* dump stack and environment to stdout */
|
||||
|
||||
struct js_StackTrace
|
||||
{
|
||||
const char *name;
|
||||
const char *file;
|
||||
int line;
|
||||
};
|
||||
|
||||
/* Exception handling */
|
||||
|
||||
struct js_Jumpbuf
|
||||
{
|
||||
jmp_buf buf;
|
||||
js_Environment *E;
|
||||
int envtop;
|
||||
int tracetop;
|
||||
int top, bot;
|
||||
int strict;
|
||||
js_Instruction *pc;
|
||||
};
|
||||
|
||||
void *js_savetrypc(js_State *J, js_Instruction *pc);
|
||||
|
||||
#define js_trypc(J, PC) \
|
||||
setjmp(js_savetrypc(J, PC))
|
||||
|
||||
/* String buffer */
|
||||
|
||||
typedef struct js_Buffer { int n, m; char s[64]; } js_Buffer;
|
||||
|
||||
void js_putc(js_State *J, js_Buffer **sbp, int c);
|
||||
void js_puts(js_State *J, js_Buffer **sb, const char *s);
|
||||
void js_putm(js_State *J, js_Buffer **sb, const char *s, const char *e);
|
||||
|
||||
/* State struct */
|
||||
|
||||
struct js_State
|
||||
{
|
||||
void *actx;
|
||||
void *uctx;
|
||||
js_Alloc alloc;
|
||||
js_Report report;
|
||||
js_Panic panic;
|
||||
|
||||
js_StringNode *strings;
|
||||
|
||||
int default_strict;
|
||||
int strict;
|
||||
|
||||
/* parser input source */
|
||||
const char *filename;
|
||||
const char *source;
|
||||
int line;
|
||||
|
||||
/* lexer state */
|
||||
struct { char *text; int len, cap; } lexbuf;
|
||||
int lexline;
|
||||
int lexchar;
|
||||
int lasttoken;
|
||||
int newline;
|
||||
|
||||
/* parser state */
|
||||
int astdepth;
|
||||
int lookahead;
|
||||
const char *text;
|
||||
double number;
|
||||
js_Ast *gcast; /* list of allocated nodes to free after parsing */
|
||||
|
||||
/* runtime environment */
|
||||
js_Object *Object_prototype;
|
||||
js_Object *Array_prototype;
|
||||
js_Object *Function_prototype;
|
||||
js_Object *Boolean_prototype;
|
||||
js_Object *Number_prototype;
|
||||
js_Object *String_prototype;
|
||||
js_Object *RegExp_prototype;
|
||||
js_Object *Date_prototype;
|
||||
|
||||
js_Object *Error_prototype;
|
||||
js_Object *EvalError_prototype;
|
||||
js_Object *RangeError_prototype;
|
||||
js_Object *ReferenceError_prototype;
|
||||
js_Object *SyntaxError_prototype;
|
||||
js_Object *TypeError_prototype;
|
||||
js_Object *URIError_prototype;
|
||||
|
||||
unsigned int seed; /* Math.random seed */
|
||||
|
||||
char scratch[12]; /* scratch buffer for iterating over array indices */
|
||||
|
||||
int nextref; /* for js_ref use */
|
||||
js_Object *R; /* registry of hidden values */
|
||||
js_Object *G; /* the global object */
|
||||
js_Environment *E; /* current environment scope */
|
||||
js_Environment *GE; /* global environment scope (at the root) */
|
||||
|
||||
/* execution stack */
|
||||
int top, bot;
|
||||
js_Value *stack;
|
||||
|
||||
/* garbage collector list */
|
||||
int gcmark;
|
||||
unsigned int gccounter, gcthresh;
|
||||
js_Environment *gcenv;
|
||||
js_Function *gcfun;
|
||||
js_Object *gcobj;
|
||||
js_String *gcstr;
|
||||
|
||||
js_Object *gcroot; /* gc scan list */
|
||||
|
||||
/* environments on the call stack but currently not in scope */
|
||||
int envtop;
|
||||
js_Environment *envstack[JS_ENVLIMIT];
|
||||
|
||||
/* debug info stack trace */
|
||||
int tracetop;
|
||||
js_StackTrace trace[JS_ENVLIMIT];
|
||||
|
||||
/* exception stack */
|
||||
int trytop;
|
||||
js_Jumpbuf trybuf[JS_TRYLIMIT];
|
||||
};
|
||||
|
||||
/* Values */
|
||||
|
||||
typedef struct js_Property js_Property;
|
||||
typedef struct js_Iterator js_Iterator;
|
||||
|
||||
/* Hint to ToPrimitive() */
|
||||
enum {
|
||||
JS_HNONE,
|
||||
JS_HNUMBER,
|
||||
JS_HSTRING
|
||||
};
|
||||
|
||||
enum js_Type {
|
||||
JS_TSHRSTR, /* type tag doubles as string zero-terminator */
|
||||
JS_TUNDEFINED,
|
||||
JS_TNULL,
|
||||
JS_TBOOLEAN,
|
||||
JS_TNUMBER,
|
||||
JS_TLITSTR,
|
||||
JS_TMEMSTR,
|
||||
JS_TOBJECT,
|
||||
};
|
||||
|
||||
enum js_Class {
|
||||
JS_COBJECT,
|
||||
JS_CARRAY,
|
||||
JS_CFUNCTION,
|
||||
JS_CSCRIPT, /* function created from global/eval code */
|
||||
JS_CCFUNCTION, /* built-in function */
|
||||
JS_CERROR,
|
||||
JS_CBOOLEAN,
|
||||
JS_CNUMBER,
|
||||
JS_CSTRING,
|
||||
JS_CREGEXP,
|
||||
JS_CDATE,
|
||||
JS_CMATH,
|
||||
JS_CJSON,
|
||||
JS_CARGUMENTS,
|
||||
JS_CITERATOR,
|
||||
JS_CUSERDATA,
|
||||
};
|
||||
|
||||
/*
|
||||
Short strings abuse the js_Value struct. By putting the type tag in the
|
||||
last byte, and using 0 as the tag for short strings, we can use the
|
||||
entire js_Value as string storage by letting the type tag serve double
|
||||
purpose as the string zero terminator.
|
||||
*/
|
||||
|
||||
union js_Value
|
||||
{
|
||||
struct {
|
||||
char pad[15];
|
||||
char type; /* type tag overlaps with final byte of shrstr */
|
||||
} t;
|
||||
union {
|
||||
char shrstr[16];
|
||||
int boolean;
|
||||
double number;
|
||||
const char *litstr;
|
||||
js_String *memstr;
|
||||
js_Object *object;
|
||||
} u;
|
||||
};
|
||||
|
||||
struct js_String
|
||||
{
|
||||
js_String *gcnext;
|
||||
char gcmark;
|
||||
char p[1];
|
||||
};
|
||||
|
||||
struct js_Regexp
|
||||
{
|
||||
void *prog;
|
||||
char *source;
|
||||
unsigned short flags;
|
||||
unsigned short last;
|
||||
};
|
||||
|
||||
struct js_Object
|
||||
{
|
||||
enum js_Class type;
|
||||
int extensible;
|
||||
js_Property *properties;
|
||||
int count; /* number of properties, for array sparseness check */
|
||||
js_Object *prototype;
|
||||
union {
|
||||
int boolean;
|
||||
double number;
|
||||
struct {
|
||||
int length;
|
||||
char *string;
|
||||
char shrstr[16];
|
||||
} s;
|
||||
struct {
|
||||
int length; /* actual length */
|
||||
int simple; /* true if array has only non-sparse array properties */
|
||||
int flat_length; /* used length of simple array part */
|
||||
int flat_capacity; /* allocated length of simple array part */
|
||||
js_Value *array;
|
||||
} a;
|
||||
struct {
|
||||
js_Function *function;
|
||||
js_Environment *scope;
|
||||
} f;
|
||||
struct {
|
||||
const char *name;
|
||||
js_CFunction function;
|
||||
js_CFunction constructor;
|
||||
int length;
|
||||
void *data;
|
||||
js_Finalize finalize;
|
||||
} c;
|
||||
js_Regexp r;
|
||||
struct {
|
||||
js_Object *target;
|
||||
int i, n; /* for array part */
|
||||
js_Iterator *head, *current; /* for object part */
|
||||
} iter;
|
||||
struct {
|
||||
const char *tag;
|
||||
void *data;
|
||||
js_HasProperty has;
|
||||
js_Put put;
|
||||
js_Delete delete;
|
||||
js_Finalize finalize;
|
||||
} user;
|
||||
} u;
|
||||
js_Object *gcnext; /* allocation list */
|
||||
js_Object *gcroot; /* scan list */
|
||||
int gcmark;
|
||||
};
|
||||
|
||||
struct js_Property
|
||||
{
|
||||
js_Property *left, *right;
|
||||
int level;
|
||||
int atts;
|
||||
js_Value value;
|
||||
js_Object *getter;
|
||||
js_Object *setter;
|
||||
char name[1];
|
||||
};
|
||||
|
||||
struct js_Iterator
|
||||
{
|
||||
js_Iterator *next;
|
||||
char name[1];
|
||||
};
|
||||
|
||||
struct js_Environment
|
||||
{
|
||||
js_Environment *outer;
|
||||
js_Object *variables;
|
||||
|
||||
js_Environment *gcnext;
|
||||
int gcmark;
|
||||
};
|
||||
|
||||
/* jsrun.c */
|
||||
js_Environment *jsR_newenvironment(js_State *J, js_Object *variables, js_Environment *outer);
|
||||
js_String *jsV_newmemstring(js_State *J, const char *s, int n);
|
||||
js_Value *js_tovalue(js_State *J, int idx);
|
||||
void js_toprimitive(js_State *J, int idx, int hint);
|
||||
js_Object *js_toobject(js_State *J, int idx);
|
||||
void js_pushvalue(js_State *J, js_Value v);
|
||||
void js_pushobject(js_State *J, js_Object *v);
|
||||
void jsR_unflattenarray(js_State *J, js_Object *obj);
|
||||
|
||||
/* jsvalue.c */
|
||||
int jsV_toboolean(js_State *J, js_Value *v);
|
||||
double jsV_tonumber(js_State *J, js_Value *v);
|
||||
double jsV_tointeger(js_State *J, js_Value *v);
|
||||
const char *jsV_tostring(js_State *J, js_Value *v);
|
||||
js_Object *jsV_toobject(js_State *J, js_Value *v);
|
||||
void jsV_toprimitive(js_State *J, js_Value *v, int preferred);
|
||||
|
||||
const char *js_itoa(char *buf, int a);
|
||||
double js_stringtofloat(const char *s, char **ep);
|
||||
int jsV_numbertointeger(double n);
|
||||
int jsV_numbertoint32(double n);
|
||||
unsigned int jsV_numbertouint32(double n);
|
||||
short jsV_numbertoint16(double n);
|
||||
unsigned short jsV_numbertouint16(double n);
|
||||
const char *jsV_numbertostring(js_State *J, char buf[32], double number);
|
||||
double jsV_stringtonumber(js_State *J, const char *string);
|
||||
|
||||
/* jsproperty.c */
|
||||
js_Object *jsV_newobject(js_State *J, enum js_Class type, js_Object *prototype);
|
||||
js_Property *jsV_getownproperty(js_State *J, js_Object *obj, const char *name);
|
||||
js_Property *jsV_getpropertyx(js_State *J, js_Object *obj, const char *name, int *own);
|
||||
js_Property *jsV_getproperty(js_State *J, js_Object *obj, const char *name);
|
||||
js_Property *jsV_setproperty(js_State *J, js_Object *obj, const char *name);
|
||||
js_Property *jsV_nextproperty(js_State *J, js_Object *obj, const char *name);
|
||||
void jsV_delproperty(js_State *J, js_Object *obj, const char *name);
|
||||
|
||||
js_Object *jsV_newiterator(js_State *J, js_Object *obj, int own);
|
||||
const char *jsV_nextiterator(js_State *J, js_Object *iter);
|
||||
|
||||
void jsV_resizearray(js_State *J, js_Object *obj, int newlen);
|
||||
|
||||
void jsV_unflattenarray(js_State *J, js_Object *obj);
|
||||
void jsV_growarray(js_State *J, js_Object *obj);
|
||||
|
||||
/* Lexer */
|
||||
|
||||
enum
|
||||
{
|
||||
TK_IDENTIFIER = 256,
|
||||
TK_NUMBER,
|
||||
TK_STRING,
|
||||
TK_REGEXP,
|
||||
|
||||
/* multi-character punctuators */
|
||||
TK_LE,
|
||||
TK_GE,
|
||||
TK_EQ,
|
||||
TK_NE,
|
||||
TK_STRICTEQ,
|
||||
TK_STRICTNE,
|
||||
TK_SHL,
|
||||
TK_SHR,
|
||||
TK_USHR,
|
||||
TK_AND,
|
||||
TK_OR,
|
||||
TK_ADD_ASS,
|
||||
TK_SUB_ASS,
|
||||
TK_MUL_ASS,
|
||||
TK_DIV_ASS,
|
||||
TK_MOD_ASS,
|
||||
TK_SHL_ASS,
|
||||
TK_SHR_ASS,
|
||||
TK_USHR_ASS,
|
||||
TK_AND_ASS,
|
||||
TK_OR_ASS,
|
||||
TK_XOR_ASS,
|
||||
TK_INC,
|
||||
TK_DEC,
|
||||
|
||||
/* keywords */
|
||||
TK_BREAK,
|
||||
TK_CASE,
|
||||
TK_CATCH,
|
||||
TK_CONTINUE,
|
||||
TK_DEBUGGER,
|
||||
TK_DEFAULT,
|
||||
TK_DELETE,
|
||||
TK_DO,
|
||||
TK_ELSE,
|
||||
TK_FALSE,
|
||||
TK_FINALLY,
|
||||
TK_FOR,
|
||||
TK_FUNCTION,
|
||||
TK_IF,
|
||||
TK_IN,
|
||||
TK_INSTANCEOF,
|
||||
TK_NEW,
|
||||
TK_NULL,
|
||||
TK_RETURN,
|
||||
TK_SWITCH,
|
||||
TK_THIS,
|
||||
TK_THROW,
|
||||
TK_TRUE,
|
||||
TK_TRY,
|
||||
TK_TYPEOF,
|
||||
TK_VAR,
|
||||
TK_VOID,
|
||||
TK_WHILE,
|
||||
TK_WITH,
|
||||
};
|
||||
|
||||
int jsY_iswhite(int c);
|
||||
int jsY_isnewline(int c);
|
||||
int jsY_ishex(int c);
|
||||
int jsY_tohex(int c);
|
||||
|
||||
const char *jsY_tokenstring(int token);
|
||||
int jsY_findword(const char *s, const char **list, int num);
|
||||
|
||||
void jsY_initlex(js_State *J, const char *filename, const char *source);
|
||||
int jsY_lex(js_State *J);
|
||||
int jsY_lexjson(js_State *J);
|
||||
|
||||
/* Parser */
|
||||
|
||||
enum js_AstType
|
||||
{
|
||||
AST_LIST,
|
||||
AST_FUNDEC,
|
||||
AST_IDENTIFIER,
|
||||
|
||||
EXP_IDENTIFIER,
|
||||
EXP_NUMBER,
|
||||
EXP_STRING,
|
||||
EXP_REGEXP,
|
||||
|
||||
/* literals */
|
||||
EXP_ELISION, /* for array elisions */
|
||||
EXP_NULL,
|
||||
EXP_TRUE,
|
||||
EXP_FALSE,
|
||||
EXP_THIS,
|
||||
|
||||
EXP_ARRAY,
|
||||
EXP_OBJECT,
|
||||
EXP_PROP_VAL,
|
||||
EXP_PROP_GET,
|
||||
EXP_PROP_SET,
|
||||
|
||||
EXP_FUN,
|
||||
|
||||
/* expressions */
|
||||
EXP_INDEX,
|
||||
EXP_MEMBER,
|
||||
EXP_CALL,
|
||||
EXP_NEW,
|
||||
|
||||
EXP_POSTINC,
|
||||
EXP_POSTDEC,
|
||||
|
||||
EXP_DELETE,
|
||||
EXP_VOID,
|
||||
EXP_TYPEOF,
|
||||
EXP_PREINC,
|
||||
EXP_PREDEC,
|
||||
EXP_POS,
|
||||
EXP_NEG,
|
||||
EXP_BITNOT,
|
||||
EXP_LOGNOT,
|
||||
|
||||
EXP_MOD,
|
||||
EXP_DIV,
|
||||
EXP_MUL,
|
||||
EXP_SUB,
|
||||
EXP_ADD,
|
||||
EXP_USHR,
|
||||
EXP_SHR,
|
||||
EXP_SHL,
|
||||
EXP_IN,
|
||||
EXP_INSTANCEOF,
|
||||
EXP_GE,
|
||||
EXP_LE,
|
||||
EXP_GT,
|
||||
EXP_LT,
|
||||
EXP_STRICTNE,
|
||||
EXP_STRICTEQ,
|
||||
EXP_NE,
|
||||
EXP_EQ,
|
||||
EXP_BITAND,
|
||||
EXP_BITXOR,
|
||||
EXP_BITOR,
|
||||
EXP_LOGAND,
|
||||
EXP_LOGOR,
|
||||
|
||||
EXP_COND,
|
||||
|
||||
EXP_ASS,
|
||||
EXP_ASS_MUL,
|
||||
EXP_ASS_DIV,
|
||||
EXP_ASS_MOD,
|
||||
EXP_ASS_ADD,
|
||||
EXP_ASS_SUB,
|
||||
EXP_ASS_SHL,
|
||||
EXP_ASS_SHR,
|
||||
EXP_ASS_USHR,
|
||||
EXP_ASS_BITAND,
|
||||
EXP_ASS_BITXOR,
|
||||
EXP_ASS_BITOR,
|
||||
|
||||
EXP_COMMA,
|
||||
|
||||
EXP_VAR, /* var initializer */
|
||||
|
||||
/* statements */
|
||||
STM_BLOCK,
|
||||
STM_EMPTY,
|
||||
STM_VAR,
|
||||
STM_IF,
|
||||
STM_DO,
|
||||
STM_WHILE,
|
||||
STM_FOR,
|
||||
STM_FOR_VAR,
|
||||
STM_FOR_IN,
|
||||
STM_FOR_IN_VAR,
|
||||
STM_CONTINUE,
|
||||
STM_BREAK,
|
||||
STM_RETURN,
|
||||
STM_WITH,
|
||||
STM_SWITCH,
|
||||
STM_THROW,
|
||||
STM_TRY,
|
||||
STM_DEBUGGER,
|
||||
|
||||
STM_LABEL,
|
||||
STM_CASE,
|
||||
STM_DEFAULT,
|
||||
};
|
||||
|
||||
typedef struct js_JumpList js_JumpList;
|
||||
|
||||
struct js_JumpList
|
||||
{
|
||||
enum js_AstType type;
|
||||
int inst;
|
||||
js_JumpList *next;
|
||||
};
|
||||
|
||||
struct js_Ast
|
||||
{
|
||||
enum js_AstType type;
|
||||
int line;
|
||||
js_Ast *parent, *a, *b, *c, *d;
|
||||
double number;
|
||||
const char *string;
|
||||
js_JumpList *jumps; /* list of break/continue jumps to patch */
|
||||
int casejump; /* for switch case clauses */
|
||||
js_Ast *gcnext; /* next in alloc list */
|
||||
};
|
||||
|
||||
js_Ast *jsP_parsefunction(js_State *J, const char *filename, const char *params, const char *body);
|
||||
js_Ast *jsP_parse(js_State *J, const char *filename, const char *source);
|
||||
void jsP_freeparse(js_State *J);
|
||||
|
||||
/* Compiler */
|
||||
|
||||
enum js_OpCode
|
||||
{
|
||||
OP_POP, /* A -- */
|
||||
OP_DUP, /* A -- A A */
|
||||
OP_DUP2, /* A B -- A B A B */
|
||||
OP_ROT2, /* A B -- B A */
|
||||
OP_ROT3, /* A B C -- C A B */
|
||||
OP_ROT4, /* A B C D -- D A B C */
|
||||
|
||||
OP_INTEGER, /* -K- (number-32768) */
|
||||
OP_NUMBER, /* -N- <number> */
|
||||
OP_STRING, /* -S- <string> */
|
||||
OP_CLOSURE, /* -F- <closure> */
|
||||
|
||||
OP_NEWARRAY,
|
||||
OP_NEWOBJECT,
|
||||
OP_NEWREGEXP, /* -S,opts- <regexp> */
|
||||
|
||||
OP_UNDEF,
|
||||
OP_NULL,
|
||||
OP_TRUE,
|
||||
OP_FALSE,
|
||||
|
||||
OP_THIS,
|
||||
OP_CURRENT, /* currently executing function object */
|
||||
|
||||
OP_GETLOCAL, /* -K- <value> */
|
||||
OP_SETLOCAL, /* <value> -K- <value> */
|
||||
OP_DELLOCAL, /* -K- false */
|
||||
|
||||
OP_HASVAR, /* -S- ( <value> | undefined ) */
|
||||
OP_GETVAR, /* -S- <value> */
|
||||
OP_SETVAR, /* <value> -S- <value> */
|
||||
OP_DELVAR, /* -S- <success> */
|
||||
|
||||
OP_IN, /* <name> <obj> -- <exists?> */
|
||||
|
||||
OP_SKIPARRAY, /* <obj> -- <obj> */
|
||||
OP_INITARRAY, /* <obj> <val> -- <obj> */
|
||||
OP_INITPROP, /* <obj> <key> <val> -- <obj> */
|
||||
OP_INITGETTER, /* <obj> <key> <closure> -- <obj> */
|
||||
OP_INITSETTER, /* <obj> <key> <closure> -- <obj> */
|
||||
|
||||
OP_GETPROP, /* <obj> <name> -- <value> */
|
||||
OP_GETPROP_S, /* <obj> -S- <value> */
|
||||
OP_SETPROP, /* <obj> <name> <value> -- <value> */
|
||||
OP_SETPROP_S, /* <obj> <value> -S- <value> */
|
||||
OP_DELPROP, /* <obj> <name> -- <success> */
|
||||
OP_DELPROP_S, /* <obj> -S- <success> */
|
||||
|
||||
OP_ITERATOR, /* <obj> -- <iobj> */
|
||||
OP_NEXTITER, /* <iobj> -- ( <iobj> <name> true | false ) */
|
||||
|
||||
OP_EVAL, /* <args...> -(numargs)- <returnvalue> */
|
||||
OP_CALL, /* <closure> <this> <args...> -(numargs)- <returnvalue> */
|
||||
OP_NEW, /* <closure> <args...> -(numargs)- <returnvalue> */
|
||||
|
||||
OP_TYPEOF,
|
||||
OP_POS,
|
||||
OP_NEG,
|
||||
OP_BITNOT,
|
||||
OP_LOGNOT,
|
||||
OP_INC, /* <x> -- ToNumber(x)+1 */
|
||||
OP_DEC, /* <x> -- ToNumber(x)-1 */
|
||||
OP_POSTINC, /* <x> -- ToNumber(x)+1 ToNumber(x) */
|
||||
OP_POSTDEC, /* <x> -- ToNumber(x)-1 ToNumber(x) */
|
||||
|
||||
OP_MUL,
|
||||
OP_DIV,
|
||||
OP_MOD,
|
||||
OP_ADD,
|
||||
OP_SUB,
|
||||
OP_SHL,
|
||||
OP_SHR,
|
||||
OP_USHR,
|
||||
OP_LT,
|
||||
OP_GT,
|
||||
OP_LE,
|
||||
OP_GE,
|
||||
OP_EQ,
|
||||
OP_NE,
|
||||
OP_STRICTEQ,
|
||||
OP_STRICTNE,
|
||||
OP_JCASE,
|
||||
OP_BITAND,
|
||||
OP_BITXOR,
|
||||
OP_BITOR,
|
||||
|
||||
OP_INSTANCEOF,
|
||||
|
||||
OP_THROW,
|
||||
|
||||
OP_TRY, /* -ADDR- /jump/ or -ADDR- <exception> */
|
||||
OP_ENDTRY,
|
||||
|
||||
OP_CATCH, /* push scope chain with exception variable */
|
||||
OP_ENDCATCH,
|
||||
|
||||
OP_WITH,
|
||||
OP_ENDWITH,
|
||||
|
||||
OP_DEBUGGER,
|
||||
OP_JUMP,
|
||||
OP_JTRUE,
|
||||
OP_JFALSE,
|
||||
OP_RETURN,
|
||||
};
|
||||
|
||||
struct js_Function
|
||||
{
|
||||
const char *name;
|
||||
int script;
|
||||
int lightweight;
|
||||
int strict;
|
||||
int arguments;
|
||||
int numparams;
|
||||
|
||||
js_Instruction *code;
|
||||
int codecap, codelen;
|
||||
|
||||
js_Function **funtab;
|
||||
int funcap, funlen;
|
||||
|
||||
const char **vartab;
|
||||
int varcap, varlen;
|
||||
|
||||
const char *filename;
|
||||
int line, lastline;
|
||||
|
||||
js_Function *gcnext;
|
||||
int gcmark;
|
||||
};
|
||||
|
||||
js_Function *jsC_compilefunction(js_State *J, js_Ast *prog);
|
||||
js_Function *jsC_compilescript(js_State *J, js_Ast *prog, int default_strict);
|
||||
|
||||
/* Builtins */
|
||||
|
||||
void jsB_init(js_State *J);
|
||||
void jsB_initobject(js_State *J);
|
||||
void jsB_initarray(js_State *J);
|
||||
void jsB_initfunction(js_State *J);
|
||||
void jsB_initboolean(js_State *J);
|
||||
void jsB_initnumber(js_State *J);
|
||||
void jsB_initstring(js_State *J);
|
||||
void jsB_initregexp(js_State *J);
|
||||
void jsB_initerror(js_State *J);
|
||||
void jsB_initmath(js_State *J);
|
||||
void jsB_initjson(js_State *J);
|
||||
void jsB_initdate(js_State *J);
|
||||
|
||||
void jsB_propf(js_State *J, const char *name, js_CFunction cfun, int n);
|
||||
void jsB_propn(js_State *J, const char *name, double number);
|
||||
void jsB_props(js_State *J, const char *name, const char *string);
|
||||
|
||||
#endif
|
137
src/mujs/jsintern.c
Normal file
137
src/mujs/jsintern.c
Normal file
|
@ -0,0 +1,137 @@
|
|||
#include "jsi.h"
|
||||
|
||||
/* Dynamically grown string buffer */
|
||||
|
||||
void js_putc(js_State *J, js_Buffer **sbp, int c)
|
||||
{
|
||||
js_Buffer *sb = *sbp;
|
||||
if (!sb) {
|
||||
sb = js_malloc(J, sizeof *sb);
|
||||
sb->n = 0;
|
||||
sb->m = sizeof sb->s;
|
||||
*sbp = sb;
|
||||
} else if (sb->n == sb->m) {
|
||||
sb = js_realloc(J, sb, (sb->m *= 2) + soffsetof(js_Buffer, s));
|
||||
*sbp = sb;
|
||||
}
|
||||
sb->s[sb->n++] = c;
|
||||
}
|
||||
|
||||
void js_puts(js_State *J, js_Buffer **sb, const char *s)
|
||||
{
|
||||
while (*s)
|
||||
js_putc(J, sb, *s++);
|
||||
}
|
||||
|
||||
void js_putm(js_State *J, js_Buffer **sb, const char *s, const char *e)
|
||||
{
|
||||
while (s < e)
|
||||
js_putc(J, sb, *s++);
|
||||
}
|
||||
|
||||
/* Use an AA-tree to quickly look up interned strings. */
|
||||
|
||||
struct js_StringNode
|
||||
{
|
||||
js_StringNode *left, *right;
|
||||
int level;
|
||||
char string[1];
|
||||
};
|
||||
|
||||
static js_StringNode jsS_sentinel = { &jsS_sentinel, &jsS_sentinel, 0, ""};
|
||||
|
||||
static js_StringNode *jsS_newstringnode(js_State *J, const char *string, const char **result)
|
||||
{
|
||||
size_t n = strlen(string);
|
||||
if (n > JS_STRLIMIT)
|
||||
js_rangeerror(J, "invalid string length");
|
||||
js_StringNode *node = js_malloc(J, soffsetof(js_StringNode, string) + n + 1);
|
||||
node->left = node->right = &jsS_sentinel;
|
||||
node->level = 1;
|
||||
memcpy(node->string, string, n + 1);
|
||||
return *result = node->string, node;
|
||||
}
|
||||
|
||||
static js_StringNode *jsS_skew(js_StringNode *node)
|
||||
{
|
||||
if (node->left->level == node->level) {
|
||||
js_StringNode *temp = node;
|
||||
node = node->left;
|
||||
temp->left = node->right;
|
||||
node->right = temp;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
static js_StringNode *jsS_split(js_StringNode *node)
|
||||
{
|
||||
if (node->right->right->level == node->level) {
|
||||
js_StringNode *temp = node;
|
||||
node = node->right;
|
||||
temp->right = node->left;
|
||||
node->left = temp;
|
||||
++node->level;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
static js_StringNode *jsS_insert(js_State *J, js_StringNode *node, const char *string, const char **result)
|
||||
{
|
||||
if (node != &jsS_sentinel) {
|
||||
int c = strcmp(string, node->string);
|
||||
if (c < 0)
|
||||
node->left = jsS_insert(J, node->left, string, result);
|
||||
else if (c > 0)
|
||||
node->right = jsS_insert(J, node->right, string, result);
|
||||
else
|
||||
return *result = node->string, node;
|
||||
node = jsS_skew(node);
|
||||
node = jsS_split(node);
|
||||
return node;
|
||||
}
|
||||
return jsS_newstringnode(J, string, result);
|
||||
}
|
||||
|
||||
static void dumpstringnode(js_StringNode *node, int level)
|
||||
{
|
||||
int i;
|
||||
if (node->left != &jsS_sentinel)
|
||||
dumpstringnode(node->left, level + 1);
|
||||
printf("%d: ", node->level);
|
||||
for (i = 0; i < level; ++i)
|
||||
putchar('\t');
|
||||
printf("'%s'\n", node->string);
|
||||
if (node->right != &jsS_sentinel)
|
||||
dumpstringnode(node->right, level + 1);
|
||||
}
|
||||
|
||||
void jsS_dumpstrings(js_State *J)
|
||||
{
|
||||
js_StringNode *root = J->strings;
|
||||
printf("interned strings {\n");
|
||||
if (root && root != &jsS_sentinel)
|
||||
dumpstringnode(root, 1);
|
||||
printf("}\n");
|
||||
}
|
||||
|
||||
static void jsS_freestringnode(js_State *J, js_StringNode *node)
|
||||
{
|
||||
if (node->left != &jsS_sentinel) jsS_freestringnode(J, node->left);
|
||||
if (node->right != &jsS_sentinel) jsS_freestringnode(J, node->right);
|
||||
js_free(J, node);
|
||||
}
|
||||
|
||||
void jsS_freestrings(js_State *J)
|
||||
{
|
||||
if (J->strings && J->strings != &jsS_sentinel)
|
||||
jsS_freestringnode(J, J->strings);
|
||||
}
|
||||
|
||||
const char *js_intern(js_State *J, const char *s)
|
||||
{
|
||||
const char *result;
|
||||
if (!J->strings)
|
||||
J->strings = &jsS_sentinel;
|
||||
J->strings = jsS_insert(J, J->strings, s, &result);
|
||||
return result;
|
||||
}
|
878
src/mujs/jslex.c
Normal file
878
src/mujs/jslex.c
Normal file
|
@ -0,0 +1,878 @@
|
|||
#include "jsi.h"
|
||||
#include "utf.h"
|
||||
|
||||
JS_NORETURN static void jsY_error(js_State *J, const char *fmt, ...) JS_PRINTFLIKE(2,3);
|
||||
|
||||
static void jsY_error(js_State *J, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char buf[512];
|
||||
char msgbuf[256];
|
||||
|
||||
va_start(ap, fmt);
|
||||
npf_vsnprintf(msgbuf, 256, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
npf_snprintf(buf, 256, "%s:%d: ", J->filename, J->lexline);
|
||||
strcat(buf, msgbuf);
|
||||
|
||||
js_newsyntaxerror(J, buf);
|
||||
js_throw(J);
|
||||
}
|
||||
|
||||
static const char *tokenstring[] = {
|
||||
"(end-of-file)",
|
||||
"'\\x01'", "'\\x02'", "'\\x03'", "'\\x04'", "'\\x05'", "'\\x06'", "'\\x07'",
|
||||
"'\\x08'", "'\\x09'", "'\\x0A'", "'\\x0B'", "'\\x0C'", "'\\x0D'", "'\\x0E'", "'\\x0F'",
|
||||
"'\\x10'", "'\\x11'", "'\\x12'", "'\\x13'", "'\\x14'", "'\\x15'", "'\\x16'", "'\\x17'",
|
||||
"'\\x18'", "'\\x19'", "'\\x1A'", "'\\x1B'", "'\\x1C'", "'\\x1D'", "'\\x1E'", "'\\x1F'",
|
||||
"' '", "'!'", "'\"'", "'#'", "'$'", "'%'", "'&'", "'\\''",
|
||||
"'('", "')'", "'*'", "'+'", "','", "'-'", "'.'", "'/'",
|
||||
"'0'", "'1'", "'2'", "'3'", "'4'", "'5'", "'6'", "'7'",
|
||||
"'8'", "'9'", "':'", "';'", "'<'", "'='", "'>'", "'?'",
|
||||
"'@'", "'A'", "'B'", "'C'", "'D'", "'E'", "'F'", "'G'",
|
||||
"'H'", "'I'", "'J'", "'K'", "'L'", "'M'", "'N'", "'O'",
|
||||
"'P'", "'Q'", "'R'", "'S'", "'T'", "'U'", "'V'", "'W'",
|
||||
"'X'", "'Y'", "'Z'", "'['", "'\'", "']'", "'^'", "'_'",
|
||||
"'`'", "'a'", "'b'", "'c'", "'d'", "'e'", "'f'", "'g'",
|
||||
"'h'", "'i'", "'j'", "'k'", "'l'", "'m'", "'n'", "'o'",
|
||||
"'p'", "'q'", "'r'", "'s'", "'t'", "'u'", "'v'", "'w'",
|
||||
"'x'", "'y'", "'z'", "'{'", "'|'", "'}'", "'~'", "'\\x7F'",
|
||||
|
||||
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
|
||||
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
|
||||
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
|
||||
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
|
||||
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
|
||||
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
|
||||
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
|
||||
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
|
||||
|
||||
"(identifier)", "(number)", "(string)", "(regexp)",
|
||||
|
||||
"'<='", "'>='", "'=='", "'!='", "'==='", "'!=='",
|
||||
"'<<'", "'>>'", "'>>>'", "'&&'", "'||'",
|
||||
"'+='", "'-='", "'*='", "'/='", "'%='",
|
||||
"'<<='", "'>>='", "'>>>='", "'&='", "'|='", "'^='",
|
||||
"'++'", "'--'",
|
||||
|
||||
"'break'", "'case'", "'catch'", "'continue'", "'debugger'",
|
||||
"'default'", "'delete'", "'do'", "'else'", "'false'", "'finally'", "'for'",
|
||||
"'function'", "'if'", "'in'", "'instanceof'", "'new'", "'null'", "'return'",
|
||||
"'switch'", "'this'", "'throw'", "'true'", "'try'", "'typeof'", "'var'",
|
||||
"'void'", "'while'", "'with'",
|
||||
};
|
||||
|
||||
const char *jsY_tokenstring(int token)
|
||||
{
|
||||
if (token >= 0 && token < (int)nelem(tokenstring))
|
||||
if (tokenstring[token])
|
||||
return tokenstring[token];
|
||||
return "<unknown>";
|
||||
}
|
||||
|
||||
static const char *keywords[] = {
|
||||
"break", "case", "catch", "continue", "debugger", "default", "delete",
|
||||
"do", "else", "false", "finally", "for", "function", "if", "in",
|
||||
"instanceof", "new", "null", "return", "switch", "this", "throw",
|
||||
"true", "try", "typeof", "var", "void", "while", "with",
|
||||
};
|
||||
|
||||
int jsY_findword(const char *s, const char **list, int num)
|
||||
{
|
||||
int l = 0;
|
||||
int r = num - 1;
|
||||
while (l <= r) {
|
||||
int m = (l + r) >> 1;
|
||||
int c = strcmp(s, list[m]);
|
||||
if (c < 0)
|
||||
r = m - 1;
|
||||
else if (c > 0)
|
||||
l = m + 1;
|
||||
else
|
||||
return m;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int jsY_findkeyword(js_State *J, const char *s)
|
||||
{
|
||||
int i = jsY_findword(s, keywords, nelem(keywords));
|
||||
if (i >= 0) {
|
||||
J->text = keywords[i];
|
||||
return TK_BREAK + i; /* first keyword + i */
|
||||
}
|
||||
J->text = js_intern(J, s);
|
||||
return TK_IDENTIFIER;
|
||||
}
|
||||
|
||||
int jsY_iswhite(int c)
|
||||
{
|
||||
return c == 0x9 || c == 0xB || c == 0xC || c == 0x20 || c == 0xA0 || c == 0xFEFF;
|
||||
}
|
||||
|
||||
int jsY_isnewline(int c)
|
||||
{
|
||||
return c == 0xA || c == 0xD || c == 0x2028 || c == 0x2029;
|
||||
}
|
||||
|
||||
#ifndef isalpha
|
||||
#define isalpha(c) ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
|
||||
#endif
|
||||
#ifndef isdigit
|
||||
#define isdigit(c) (c >= '0' && c <= '9')
|
||||
#endif
|
||||
#ifndef ishex
|
||||
#define ishex(c) ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))
|
||||
#endif
|
||||
|
||||
static int jsY_isidentifierstart(int c)
|
||||
{
|
||||
return isalpha(c) || c == '$' || c == '_' || isalpharune(c);
|
||||
}
|
||||
|
||||
static int jsY_isidentifierpart(int c)
|
||||
{
|
||||
return isdigit(c) || isalpha(c) || c == '$' || c == '_' || isalpharune(c);
|
||||
}
|
||||
|
||||
static int jsY_isdec(int c)
|
||||
{
|
||||
return isdigit(c);
|
||||
}
|
||||
|
||||
int jsY_ishex(int c)
|
||||
{
|
||||
return isdigit(c) || ishex(c);
|
||||
}
|
||||
|
||||
int jsY_tohex(int c)
|
||||
{
|
||||
if (c >= '0' && c <= '9') return c - '0';
|
||||
if (c >= 'a' && c <= 'f') return c - 'a' + 0xA;
|
||||
if (c >= 'A' && c <= 'F') return c - 'A' + 0xA;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void jsY_next(js_State *J)
|
||||
{
|
||||
Rune c;
|
||||
if (*J->source == 0) {
|
||||
J->lexchar = EOF;
|
||||
return;
|
||||
}
|
||||
J->source += chartorune(&c, J->source);
|
||||
/* consume CR LF as one unit */
|
||||
if (c == '\r' && *J->source == '\n')
|
||||
++J->source;
|
||||
if (jsY_isnewline(c)) {
|
||||
J->line++;
|
||||
c = '\n';
|
||||
}
|
||||
J->lexchar = c;
|
||||
}
|
||||
|
||||
#define jsY_accept(J, x) (J->lexchar == x ? (jsY_next(J), 1) : 0)
|
||||
|
||||
#define jsY_expect(J, x) if (!jsY_accept(J, x)) jsY_error(J, "expected '%c'", x)
|
||||
|
||||
static void jsY_unescape(js_State *J)
|
||||
{
|
||||
if (jsY_accept(J, '\\')) {
|
||||
if (jsY_accept(J, 'u')) {
|
||||
int x = 0;
|
||||
if (!jsY_ishex(J->lexchar)) { goto error; } x |= jsY_tohex(J->lexchar) << 12; jsY_next(J);
|
||||
if (!jsY_ishex(J->lexchar)) { goto error; } x |= jsY_tohex(J->lexchar) << 8; jsY_next(J);
|
||||
if (!jsY_ishex(J->lexchar)) { goto error; } x |= jsY_tohex(J->lexchar) << 4; jsY_next(J);
|
||||
if (!jsY_ishex(J->lexchar)) { goto error; } x |= jsY_tohex(J->lexchar);
|
||||
J->lexchar = x;
|
||||
return;
|
||||
}
|
||||
error:
|
||||
jsY_error(J, "unexpected escape sequence");
|
||||
}
|
||||
}
|
||||
|
||||
static void textinit(js_State *J)
|
||||
{
|
||||
if (!J->lexbuf.text) {
|
||||
J->lexbuf.cap = 4096;
|
||||
J->lexbuf.text = js_malloc(J, J->lexbuf.cap);
|
||||
}
|
||||
J->lexbuf.len = 0;
|
||||
}
|
||||
|
||||
static void textpush(js_State *J, Rune c)
|
||||
{
|
||||
int n;
|
||||
if (c == EOF)
|
||||
n = 1;
|
||||
else
|
||||
n = runelen(c);
|
||||
if (J->lexbuf.len + n > J->lexbuf.cap) {
|
||||
J->lexbuf.cap = J->lexbuf.cap * 2;
|
||||
J->lexbuf.text = js_realloc(J, J->lexbuf.text, J->lexbuf.cap);
|
||||
}
|
||||
if (c == EOF)
|
||||
J->lexbuf.text[J->lexbuf.len++] = 0;
|
||||
else
|
||||
J->lexbuf.len += runetochar(J->lexbuf.text + J->lexbuf.len, &c);
|
||||
}
|
||||
|
||||
static char *textend(js_State *J)
|
||||
{
|
||||
textpush(J, EOF);
|
||||
return J->lexbuf.text;
|
||||
}
|
||||
|
||||
static void lexlinecomment(js_State *J)
|
||||
{
|
||||
while (J->lexchar != EOF && J->lexchar != '\n')
|
||||
jsY_next(J);
|
||||
}
|
||||
|
||||
static int lexcomment(js_State *J)
|
||||
{
|
||||
/* already consumed initial '/' '*' sequence */
|
||||
while (J->lexchar != EOF) {
|
||||
if (jsY_accept(J, '*')) {
|
||||
while (J->lexchar == '*')
|
||||
jsY_next(J);
|
||||
if (jsY_accept(J, '/'))
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
jsY_next(J);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static double lexhex(js_State *J)
|
||||
{
|
||||
double n = 0;
|
||||
if (!jsY_ishex(J->lexchar))
|
||||
jsY_error(J, "malformed hexadecimal number");
|
||||
while (jsY_ishex(J->lexchar)) {
|
||||
n = n * 16 + jsY_tohex(J->lexchar);
|
||||
jsY_next(J);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
static double lexinteger(js_State *J)
|
||||
{
|
||||
double n = 0;
|
||||
if (!jsY_isdec(J->lexchar))
|
||||
jsY_error(J, "malformed number");
|
||||
while (jsY_isdec(J->lexchar)) {
|
||||
n = n * 10 + (J->lexchar - '0');
|
||||
jsY_next(J);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static double lexfraction(js_State *J)
|
||||
{
|
||||
double n = 0;
|
||||
double d = 1;
|
||||
while (jsY_isdec(J->lexchar)) {
|
||||
n = n * 10 + (J->lexchar - '0');
|
||||
d = d * 10;
|
||||
jsY_next(J);
|
||||
}
|
||||
return n / d;
|
||||
}
|
||||
|
||||
static double lexexponent(js_State *J)
|
||||
{
|
||||
double sign;
|
||||
if (jsY_accept(J, 'e') || jsY_accept(J, 'E')) {
|
||||
if (jsY_accept(J, '-')) sign = -1;
|
||||
else if (jsY_accept(J, '+')) sign = 1;
|
||||
else sign = 1;
|
||||
return sign * lexinteger(J);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lexnumber(js_State *J)
|
||||
{
|
||||
double n;
|
||||
double e;
|
||||
|
||||
if (jsY_accept(J, '0')) {
|
||||
if (jsY_accept(J, 'x') || jsY_accept(J, 'X')) {
|
||||
J->number = lexhex(J);
|
||||
return TK_NUMBER;
|
||||
}
|
||||
if (jsY_isdec(J->lexchar))
|
||||
jsY_error(J, "number with leading zero");
|
||||
n = 0;
|
||||
if (jsY_accept(J, '.'))
|
||||
n += lexfraction(J);
|
||||
} else if (jsY_accept(J, '.')) {
|
||||
if (!jsY_isdec(J->lexchar))
|
||||
return '.';
|
||||
n = lexfraction(J);
|
||||
} else {
|
||||
n = lexinteger(J);
|
||||
if (jsY_accept(J, '.'))
|
||||
n += lexfraction(J);
|
||||
}
|
||||
|
||||
e = lexexponent(J);
|
||||
if (e < 0)
|
||||
n /= pow(10, -e);
|
||||
else if (e > 0)
|
||||
n *= pow(10, e);
|
||||
|
||||
if (jsY_isidentifierstart(J->lexchar))
|
||||
jsY_error(J, "number with letter suffix");
|
||||
|
||||
J->number = n;
|
||||
return TK_NUMBER;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int lexnumber(js_State *J)
|
||||
{
|
||||
const char *s = J->source - 1;
|
||||
|
||||
if (jsY_accept(J, '0')) {
|
||||
if (jsY_accept(J, 'x') || jsY_accept(J, 'X')) {
|
||||
J->number = lexhex(J);
|
||||
return TK_NUMBER;
|
||||
}
|
||||
if (jsY_isdec(J->lexchar))
|
||||
jsY_error(J, "number with leading zero");
|
||||
if (jsY_accept(J, '.')) {
|
||||
while (jsY_isdec(J->lexchar))
|
||||
jsY_next(J);
|
||||
}
|
||||
} else if (jsY_accept(J, '.')) {
|
||||
if (!jsY_isdec(J->lexchar))
|
||||
return '.';
|
||||
while (jsY_isdec(J->lexchar))
|
||||
jsY_next(J);
|
||||
} else {
|
||||
while (jsY_isdec(J->lexchar))
|
||||
jsY_next(J);
|
||||
if (jsY_accept(J, '.')) {
|
||||
while (jsY_isdec(J->lexchar))
|
||||
jsY_next(J);
|
||||
}
|
||||
}
|
||||
|
||||
if (jsY_accept(J, 'e') || jsY_accept(J, 'E')) {
|
||||
if (J->lexchar == '-' || J->lexchar == '+')
|
||||
jsY_next(J);
|
||||
if (jsY_isdec(J->lexchar))
|
||||
while (jsY_isdec(J->lexchar))
|
||||
jsY_next(J);
|
||||
else
|
||||
jsY_error(J, "missing exponent");
|
||||
}
|
||||
|
||||
if (jsY_isidentifierstart(J->lexchar))
|
||||
jsY_error(J, "number with letter suffix");
|
||||
|
||||
J->number = js_strtod(s, NULL);
|
||||
return TK_NUMBER;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int lexescape(js_State *J)
|
||||
{
|
||||
int x = 0;
|
||||
|
||||
/* already consumed '\' */
|
||||
|
||||
if (jsY_accept(J, '\n'))
|
||||
return 0;
|
||||
|
||||
switch (J->lexchar) {
|
||||
case EOF: jsY_error(J, "unterminated escape sequence");
|
||||
case 'u':
|
||||
jsY_next(J);
|
||||
if (!jsY_ishex(J->lexchar)) return 1; else { x |= jsY_tohex(J->lexchar) << 12; jsY_next(J); }
|
||||
if (!jsY_ishex(J->lexchar)) return 1; else { x |= jsY_tohex(J->lexchar) << 8; jsY_next(J); }
|
||||
if (!jsY_ishex(J->lexchar)) return 1; else { x |= jsY_tohex(J->lexchar) << 4; jsY_next(J); }
|
||||
if (!jsY_ishex(J->lexchar)) return 1; else { x |= jsY_tohex(J->lexchar); jsY_next(J); }
|
||||
textpush(J, x);
|
||||
break;
|
||||
case 'x':
|
||||
jsY_next(J);
|
||||
if (!jsY_ishex(J->lexchar)) return 1; else { x |= jsY_tohex(J->lexchar) << 4; jsY_next(J); }
|
||||
if (!jsY_ishex(J->lexchar)) return 1; else { x |= jsY_tohex(J->lexchar); jsY_next(J); }
|
||||
textpush(J, x);
|
||||
break;
|
||||
case '0': textpush(J, 0); jsY_next(J); break;
|
||||
case '\\': textpush(J, '\\'); jsY_next(J); break;
|
||||
case '\'': textpush(J, '\''); jsY_next(J); break;
|
||||
case '"': textpush(J, '"'); jsY_next(J); break;
|
||||
case 'b': textpush(J, '\b'); jsY_next(J); break;
|
||||
case 'f': textpush(J, '\f'); jsY_next(J); break;
|
||||
case 'n': textpush(J, '\n'); jsY_next(J); break;
|
||||
case 'r': textpush(J, '\r'); jsY_next(J); break;
|
||||
case 't': textpush(J, '\t'); jsY_next(J); break;
|
||||
case 'v': textpush(J, '\v'); jsY_next(J); break;
|
||||
default: textpush(J, J->lexchar); jsY_next(J); break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lexstring(js_State *J)
|
||||
{
|
||||
const char *s;
|
||||
|
||||
int q = J->lexchar;
|
||||
jsY_next(J);
|
||||
|
||||
textinit(J);
|
||||
|
||||
while (J->lexchar != q) {
|
||||
if (J->lexchar == EOF || J->lexchar == '\n')
|
||||
jsY_error(J, "string not terminated");
|
||||
if (jsY_accept(J, '\\')) {
|
||||
if (lexescape(J))
|
||||
jsY_error(J, "malformed escape sequence");
|
||||
} else {
|
||||
textpush(J, J->lexchar);
|
||||
jsY_next(J);
|
||||
}
|
||||
}
|
||||
jsY_expect(J, q);
|
||||
|
||||
s = textend(J);
|
||||
|
||||
J->text = js_intern(J, s);
|
||||
return TK_STRING;
|
||||
}
|
||||
|
||||
/* the ugliest language wart ever... */
|
||||
static int isregexpcontext(int last)
|
||||
{
|
||||
switch (last) {
|
||||
case ']':
|
||||
case ')':
|
||||
case '}':
|
||||
case TK_IDENTIFIER:
|
||||
case TK_NUMBER:
|
||||
case TK_STRING:
|
||||
case TK_FALSE:
|
||||
case TK_NULL:
|
||||
case TK_THIS:
|
||||
case TK_TRUE:
|
||||
return 0;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int lexregexp(js_State *J)
|
||||
{
|
||||
const char *s;
|
||||
int g, m, i;
|
||||
int inclass = 0;
|
||||
|
||||
/* already consumed initial '/' */
|
||||
|
||||
textinit(J);
|
||||
|
||||
/* regexp body */
|
||||
while (J->lexchar != '/' || inclass) {
|
||||
if (J->lexchar == EOF || J->lexchar == '\n') {
|
||||
jsY_error(J, "regular expression not terminated");
|
||||
} else if (jsY_accept(J, '\\')) {
|
||||
if (jsY_accept(J, '/')) {
|
||||
textpush(J, '/');
|
||||
} else {
|
||||
textpush(J, '\\');
|
||||
if (J->lexchar == EOF || J->lexchar == '\n')
|
||||
jsY_error(J, "regular expression not terminated");
|
||||
textpush(J, J->lexchar);
|
||||
jsY_next(J);
|
||||
}
|
||||
} else {
|
||||
if (J->lexchar == '[' && !inclass)
|
||||
inclass = 1;
|
||||
if (J->lexchar == ']' && inclass)
|
||||
inclass = 0;
|
||||
textpush(J, J->lexchar);
|
||||
jsY_next(J);
|
||||
}
|
||||
}
|
||||
jsY_expect(J, '/');
|
||||
|
||||
s = textend(J);
|
||||
|
||||
/* regexp flags */
|
||||
g = i = m = 0;
|
||||
|
||||
while (jsY_isidentifierpart(J->lexchar)) {
|
||||
if (jsY_accept(J, 'g')) ++g;
|
||||
else if (jsY_accept(J, 'i')) ++i;
|
||||
else if (jsY_accept(J, 'm')) ++m;
|
||||
else jsY_error(J, "illegal flag in regular expression: %c", J->lexchar);
|
||||
}
|
||||
|
||||
if (g > 1 || i > 1 || m > 1)
|
||||
jsY_error(J, "duplicated flag in regular expression");
|
||||
|
||||
J->text = js_intern(J, s);
|
||||
J->number = 0;
|
||||
if (g) J->number += JS_REGEXP_G;
|
||||
if (i) J->number += JS_REGEXP_I;
|
||||
if (m) J->number += JS_REGEXP_M;
|
||||
return TK_REGEXP;
|
||||
}
|
||||
|
||||
/* simple "return [no Line Terminator here] ..." contexts */
|
||||
static int isnlthcontext(int last)
|
||||
{
|
||||
switch (last) {
|
||||
case TK_BREAK:
|
||||
case TK_CONTINUE:
|
||||
case TK_RETURN:
|
||||
case TK_THROW:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int jsY_lexx(js_State *J)
|
||||
{
|
||||
J->newline = 0;
|
||||
|
||||
while (1) {
|
||||
J->lexline = J->line; /* save location of beginning of token */
|
||||
|
||||
while (jsY_iswhite(J->lexchar))
|
||||
jsY_next(J);
|
||||
|
||||
if (jsY_accept(J, '\n')) {
|
||||
J->newline = 1;
|
||||
if (isnlthcontext(J->lasttoken))
|
||||
return ';';
|
||||
continue;
|
||||
}
|
||||
|
||||
if (jsY_accept(J, '/')) {
|
||||
if (jsY_accept(J, '/')) {
|
||||
lexlinecomment(J);
|
||||
continue;
|
||||
} else if (jsY_accept(J, '*')) {
|
||||
if (lexcomment(J))
|
||||
jsY_error(J, "multi-line comment not terminated");
|
||||
continue;
|
||||
} else if (isregexpcontext(J->lasttoken)) {
|
||||
return lexregexp(J);
|
||||
} else if (jsY_accept(J, '=')) {
|
||||
return TK_DIV_ASS;
|
||||
} else {
|
||||
return '/';
|
||||
}
|
||||
}
|
||||
|
||||
if (J->lexchar >= '0' && J->lexchar <= '9') {
|
||||
return lexnumber(J);
|
||||
}
|
||||
|
||||
switch (J->lexchar) {
|
||||
case '(': jsY_next(J); return '(';
|
||||
case ')': jsY_next(J); return ')';
|
||||
case ',': jsY_next(J); return ',';
|
||||
case ':': jsY_next(J); return ':';
|
||||
case ';': jsY_next(J); return ';';
|
||||
case '?': jsY_next(J); return '?';
|
||||
case '[': jsY_next(J); return '[';
|
||||
case ']': jsY_next(J); return ']';
|
||||
case '{': jsY_next(J); return '{';
|
||||
case '}': jsY_next(J); return '}';
|
||||
case '~': jsY_next(J); return '~';
|
||||
|
||||
case '\'':
|
||||
case '"':
|
||||
return lexstring(J);
|
||||
|
||||
case '.':
|
||||
return lexnumber(J);
|
||||
|
||||
case '<':
|
||||
jsY_next(J);
|
||||
if (jsY_accept(J, '<')) {
|
||||
if (jsY_accept(J, '='))
|
||||
return TK_SHL_ASS;
|
||||
return TK_SHL;
|
||||
}
|
||||
if (jsY_accept(J, '='))
|
||||
return TK_LE;
|
||||
return '<';
|
||||
|
||||
case '>':
|
||||
jsY_next(J);
|
||||
if (jsY_accept(J, '>')) {
|
||||
if (jsY_accept(J, '>')) {
|
||||
if (jsY_accept(J, '='))
|
||||
return TK_USHR_ASS;
|
||||
return TK_USHR;
|
||||
}
|
||||
if (jsY_accept(J, '='))
|
||||
return TK_SHR_ASS;
|
||||
return TK_SHR;
|
||||
}
|
||||
if (jsY_accept(J, '='))
|
||||
return TK_GE;
|
||||
return '>';
|
||||
|
||||
case '=':
|
||||
jsY_next(J);
|
||||
if (jsY_accept(J, '=')) {
|
||||
if (jsY_accept(J, '='))
|
||||
return TK_STRICTEQ;
|
||||
return TK_EQ;
|
||||
}
|
||||
return '=';
|
||||
|
||||
case '!':
|
||||
jsY_next(J);
|
||||
if (jsY_accept(J, '=')) {
|
||||
if (jsY_accept(J, '='))
|
||||
return TK_STRICTNE;
|
||||
return TK_NE;
|
||||
}
|
||||
return '!';
|
||||
|
||||
case '+':
|
||||
jsY_next(J);
|
||||
if (jsY_accept(J, '+'))
|
||||
return TK_INC;
|
||||
if (jsY_accept(J, '='))
|
||||
return TK_ADD_ASS;
|
||||
return '+';
|
||||
|
||||
case '-':
|
||||
jsY_next(J);
|
||||
if (jsY_accept(J, '-'))
|
||||
return TK_DEC;
|
||||
if (jsY_accept(J, '='))
|
||||
return TK_SUB_ASS;
|
||||
return '-';
|
||||
|
||||
case '*':
|
||||
jsY_next(J);
|
||||
if (jsY_accept(J, '='))
|
||||
return TK_MUL_ASS;
|
||||
return '*';
|
||||
|
||||
case '%':
|
||||
jsY_next(J);
|
||||
if (jsY_accept(J, '='))
|
||||
return TK_MOD_ASS;
|
||||
return '%';
|
||||
|
||||
case '&':
|
||||
jsY_next(J);
|
||||
if (jsY_accept(J, '&'))
|
||||
return TK_AND;
|
||||
if (jsY_accept(J, '='))
|
||||
return TK_AND_ASS;
|
||||
return '&';
|
||||
|
||||
case '|':
|
||||
jsY_next(J);
|
||||
if (jsY_accept(J, '|'))
|
||||
return TK_OR;
|
||||
if (jsY_accept(J, '='))
|
||||
return TK_OR_ASS;
|
||||
return '|';
|
||||
|
||||
case '^':
|
||||
jsY_next(J);
|
||||
if (jsY_accept(J, '='))
|
||||
return TK_XOR_ASS;
|
||||
return '^';
|
||||
|
||||
case EOF:
|
||||
return 0; /* EOF */
|
||||
}
|
||||
|
||||
/* Handle \uXXXX escapes in identifiers */
|
||||
jsY_unescape(J);
|
||||
if (jsY_isidentifierstart(J->lexchar)) {
|
||||
textinit(J);
|
||||
textpush(J, J->lexchar);
|
||||
|
||||
jsY_next(J);
|
||||
jsY_unescape(J);
|
||||
while (jsY_isidentifierpart(J->lexchar)) {
|
||||
textpush(J, J->lexchar);
|
||||
jsY_next(J);
|
||||
jsY_unescape(J);
|
||||
}
|
||||
|
||||
textend(J);
|
||||
|
||||
return jsY_findkeyword(J, J->lexbuf.text);
|
||||
}
|
||||
|
||||
if (J->lexchar >= 0x20 && J->lexchar <= 0x7E)
|
||||
jsY_error(J, "unexpected character: '%c'", J->lexchar);
|
||||
jsY_error(J, "unexpected character: \\u%04X", J->lexchar);
|
||||
}
|
||||
}
|
||||
|
||||
void jsY_initlex(js_State *J, const char *filename, const char *source)
|
||||
{
|
||||
J->filename = filename;
|
||||
J->source = source;
|
||||
J->line = 1;
|
||||
J->lasttoken = 0;
|
||||
jsY_next(J); /* load first lookahead character */
|
||||
}
|
||||
|
||||
int jsY_lex(js_State *J)
|
||||
{
|
||||
return J->lasttoken = jsY_lexx(J);
|
||||
}
|
||||
|
||||
static int lexjsonnumber(js_State *J)
|
||||
{
|
||||
const char *s = J->source - 1;
|
||||
|
||||
if (J->lexchar == '-')
|
||||
jsY_next(J);
|
||||
|
||||
if (J->lexchar == '0')
|
||||
jsY_next(J);
|
||||
else if (J->lexchar >= '1' && J->lexchar <= '9')
|
||||
while (isdigit(J->lexchar))
|
||||
jsY_next(J);
|
||||
else
|
||||
jsY_error(J, "unexpected non-digit");
|
||||
|
||||
if (jsY_accept(J, '.')) {
|
||||
if (isdigit(J->lexchar))
|
||||
while (isdigit(J->lexchar))
|
||||
jsY_next(J);
|
||||
else
|
||||
jsY_error(J, "missing digits after decimal point");
|
||||
}
|
||||
|
||||
if (jsY_accept(J, 'e') || jsY_accept(J, 'E')) {
|
||||
if (J->lexchar == '-' || J->lexchar == '+')
|
||||
jsY_next(J);
|
||||
if (isdigit(J->lexchar))
|
||||
while (isdigit(J->lexchar))
|
||||
jsY_next(J);
|
||||
else
|
||||
jsY_error(J, "missing digits after exponent indicator");
|
||||
}
|
||||
|
||||
J->number = js_strtod(s, NULL);
|
||||
return TK_NUMBER;
|
||||
}
|
||||
|
||||
static int lexjsonescape(js_State *J)
|
||||
{
|
||||
int x = 0;
|
||||
|
||||
/* already consumed '\' */
|
||||
|
||||
switch (J->lexchar) {
|
||||
default: jsY_error(J, "invalid escape sequence");
|
||||
case 'u':
|
||||
jsY_next(J);
|
||||
if (!jsY_ishex(J->lexchar)) return 1; else { x |= jsY_tohex(J->lexchar) << 12; jsY_next(J); }
|
||||
if (!jsY_ishex(J->lexchar)) return 1; else { x |= jsY_tohex(J->lexchar) << 8; jsY_next(J); }
|
||||
if (!jsY_ishex(J->lexchar)) return 1; else { x |= jsY_tohex(J->lexchar) << 4; jsY_next(J); }
|
||||
if (!jsY_ishex(J->lexchar)) return 1; else { x |= jsY_tohex(J->lexchar); jsY_next(J); }
|
||||
textpush(J, x);
|
||||
break;
|
||||
case '"': textpush(J, '"'); jsY_next(J); break;
|
||||
case '\\': textpush(J, '\\'); jsY_next(J); break;
|
||||
case '/': textpush(J, '/'); jsY_next(J); break;
|
||||
case 'b': textpush(J, '\b'); jsY_next(J); break;
|
||||
case 'f': textpush(J, '\f'); jsY_next(J); break;
|
||||
case 'n': textpush(J, '\n'); jsY_next(J); break;
|
||||
case 'r': textpush(J, '\r'); jsY_next(J); break;
|
||||
case 't': textpush(J, '\t'); jsY_next(J); break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lexjsonstring(js_State *J)
|
||||
{
|
||||
const char *s;
|
||||
|
||||
textinit(J);
|
||||
|
||||
while (J->lexchar != '"') {
|
||||
if (J->lexchar == EOF)
|
||||
jsY_error(J, "unterminated string");
|
||||
else if (J->lexchar < 32)
|
||||
jsY_error(J, "invalid control character in string");
|
||||
else if (jsY_accept(J, '\\'))
|
||||
lexjsonescape(J);
|
||||
else {
|
||||
textpush(J, J->lexchar);
|
||||
jsY_next(J);
|
||||
}
|
||||
}
|
||||
jsY_expect(J, '"');
|
||||
|
||||
s = textend(J);
|
||||
|
||||
J->text = js_intern(J, s);
|
||||
return TK_STRING;
|
||||
}
|
||||
|
||||
int jsY_lexjson(js_State *J)
|
||||
{
|
||||
while (1) {
|
||||
J->lexline = J->line; /* save location of beginning of token */
|
||||
|
||||
while (jsY_iswhite(J->lexchar) || J->lexchar == '\n')
|
||||
jsY_next(J);
|
||||
|
||||
if ((J->lexchar >= '0' && J->lexchar <= '9') || J->lexchar == '-')
|
||||
return lexjsonnumber(J);
|
||||
|
||||
switch (J->lexchar) {
|
||||
case ',': jsY_next(J); return ',';
|
||||
case ':': jsY_next(J); return ':';
|
||||
case '[': jsY_next(J); return '[';
|
||||
case ']': jsY_next(J); return ']';
|
||||
case '{': jsY_next(J); return '{';
|
||||
case '}': jsY_next(J); return '}';
|
||||
|
||||
case '"':
|
||||
jsY_next(J);
|
||||
return lexjsonstring(J);
|
||||
|
||||
case 'f':
|
||||
jsY_next(J); jsY_expect(J, 'a'); jsY_expect(J, 'l'); jsY_expect(J, 's'); jsY_expect(J, 'e');
|
||||
return TK_FALSE;
|
||||
|
||||
case 'n':
|
||||
jsY_next(J); jsY_expect(J, 'u'); jsY_expect(J, 'l'); jsY_expect(J, 'l');
|
||||
return TK_NULL;
|
||||
|
||||
case 't':
|
||||
jsY_next(J); jsY_expect(J, 'r'); jsY_expect(J, 'u'); jsY_expect(J, 'e');
|
||||
return TK_TRUE;
|
||||
|
||||
case EOF:
|
||||
return 0; /* EOF */
|
||||
}
|
||||
|
||||
if (J->lexchar >= 0x20 && J->lexchar <= 0x7E)
|
||||
jsY_error(J, "unexpected character: '%c'", J->lexchar);
|
||||
jsY_error(J, "unexpected character: \\u%04X", J->lexchar);
|
||||
}
|
||||
}
|
280
src/mujs/jslibtemple.c
Normal file
280
src/mujs/jslibtemple.c
Normal file
|
@ -0,0 +1,280 @@
|
|||
#include <stdbool.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "../libtemple/os.h"
|
||||
|
||||
#define value_or_number(v, x) js_isundefined(J, x) ? v : js_tonumber(J, x)
|
||||
#define null_or_number(x) value_or_number(0, x)
|
||||
|
||||
/** DC **/
|
||||
|
||||
// public CDC *DCAlias(CDC *dc=NULL,CTask *task=NULL)
|
||||
void DC_alias(js_State *J)
|
||||
{
|
||||
long dc = null_or_number(1);
|
||||
long task = null_or_number(2);
|
||||
js_pushnumber(J, os_call_ext_str_2("DCAlias", dc, task));
|
||||
}
|
||||
|
||||
// public U0 DCClear(CDC *dc=NULL)
|
||||
void DC_clear(js_State *J)
|
||||
{
|
||||
long dc = null_or_number(1);
|
||||
os_call_ext_str_1("DCClear", dc);
|
||||
js_pushundefined(J);
|
||||
}
|
||||
|
||||
// public CDC *DCCopy(CDC *dc,CTask *task=NULL)
|
||||
void DC_copy(js_State *J)
|
||||
{
|
||||
long dc = null_or_number(1);
|
||||
long task = null_or_number(2);
|
||||
js_pushnumber(J, os_call_ext_str_2("DCCopy", dc, task));
|
||||
}
|
||||
|
||||
// public U0 DCDel(CDC *dc)
|
||||
void DC_delete(js_State *J)
|
||||
{
|
||||
if (!js_isundefined(J, 1)) {
|
||||
os_call_ext_str_1("DCDel", js_tonumber(J, 1));
|
||||
}
|
||||
js_pushundefined(J);
|
||||
}
|
||||
|
||||
// public U0 DCFill(CDC *dc=NULL,CColorROPU32 val=TRANSPARENT)
|
||||
void DC_fill(js_State *J)
|
||||
{
|
||||
long dc = null_or_number(1);
|
||||
long color = value_or_number(255, 2);
|
||||
os_call_ext_str_2("DCFill", dc, color);
|
||||
js_pushundefined(J);
|
||||
}
|
||||
|
||||
// public CDC *DCNew(I64 width,I64 height,CTask *task=NULL,Bool null_bitmap=FALSE)
|
||||
void DC_new(js_State *J)
|
||||
{
|
||||
if (js_isundefined(J, 1) || js_isundefined(J, 2)) {
|
||||
js_pushundefined(J);
|
||||
return;
|
||||
}
|
||||
long width = js_tonumber(J, 1);
|
||||
long height = js_tonumber(J, 2);
|
||||
long task = null_or_number(3);
|
||||
long null_bitmap = null_or_number(4);
|
||||
js_pushnumber(J, os_call_ext_str_4("DCNew", width, height, task, null_bitmap));
|
||||
}
|
||||
|
||||
// public I64 DCColorChg(CDC *dc,I64 src_color,I64 dst_color=TRANSPARENT)
|
||||
void DC_replace_color(js_State *J)
|
||||
{
|
||||
if (js_isundefined(J, 1) || js_isundefined(J, 2)) {
|
||||
js_pushundefined(J);
|
||||
return;
|
||||
}
|
||||
long dc = js_tonumber(J, 1);
|
||||
long src_color = js_tonumber(J, 2);
|
||||
long dst_color = value_or_number(255, 3);
|
||||
js_pushnumber(J, os_call_ext_str_3("DCColorChg", dc, src_color, dst_color));
|
||||
}
|
||||
|
||||
// DC.set_color(dc, color)
|
||||
void DC_set_color(js_State *J)
|
||||
{
|
||||
if (!js_isundefined(J, 1) && !js_isundefined(J, 2)) {
|
||||
long dc = js_tonumber(J, 1);
|
||||
long color = js_tonumber(J, 2);
|
||||
memset((void*)(dc + 0xa0), color, 1); // offset(CDC.color) == 0xa0
|
||||
}
|
||||
js_pushundefined(J);
|
||||
}
|
||||
|
||||
void jsLT_initdc(js_State *J)
|
||||
{
|
||||
js_pushobject(J, jsV_newobject(J, -1, J->Object_prototype));
|
||||
{
|
||||
jsB_propf(J, "DC.alias", DC_alias, 1);
|
||||
jsB_propf(J, "DC.clear", DC_clear, 1);
|
||||
jsB_propf(J, "DC.copy", DC_copy, 4);
|
||||
jsB_propf(J, "DC.delete", DC_delete, 1);
|
||||
jsB_propf(J, "DC.fill", DC_fill, 2);
|
||||
jsB_propf(J, "DC.new", DC_new, 2);
|
||||
jsB_propf(J, "DC.replace_color", DC_replace_color, 3);
|
||||
jsB_propf(J, "DC.set_color", DC_set_color, 2);
|
||||
}
|
||||
js_defglobal(J, "DC", JS_DONTENUM);
|
||||
}
|
||||
|
||||
/** Gr **/
|
||||
|
||||
// public I64 GrBlot(CDC *dc=gr.dc,I64 x,I64 y,CDC *img)
|
||||
void Gr_blot(js_State *J)
|
||||
{
|
||||
if (js_isundefined(J, 1) || js_isundefined(J, 2) || js_isundefined(J, 3) || js_isundefined(J, 4)) {
|
||||
js_pushundefined(J);
|
||||
return;
|
||||
}
|
||||
long dc = js_tonumber(J, 1);
|
||||
long x = js_tonumber(J, 2);
|
||||
long y = js_tonumber(J, 3);
|
||||
long img = js_tonumber(J, 4);
|
||||
js_pushnumber(J, os_call_ext_str_4("GrBlot", dc, x, y, img));
|
||||
}
|
||||
|
||||
// public I64 GrPeek(CDC *dc=gr.dc,I64 x,I64 y)
|
||||
void Gr_peek(js_State *J)
|
||||
{
|
||||
if (js_isundefined(J, 1) || js_isundefined(J, 2) || js_isundefined(J, 3)) {
|
||||
js_pushundefined(J);
|
||||
return;
|
||||
}
|
||||
long dc = js_tonumber(J, 1);
|
||||
long x = js_tonumber(J, 2);
|
||||
long y = js_tonumber(J, 3);
|
||||
js_pushnumber(J, os_call_ext_str_3("GrPeek", dc, x, y));
|
||||
}
|
||||
|
||||
// public Bool GrPlot(CDC *dc=gr.dc,I64 x,I64 y)
|
||||
void Gr_plot(js_State *J)
|
||||
{
|
||||
if (js_isundefined(J, 1) || js_isundefined(J, 2) || js_isundefined(J, 3)) {
|
||||
js_pushundefined(J);
|
||||
return;
|
||||
}
|
||||
long dc = js_tonumber(J, 1);
|
||||
long x = js_tonumber(J, 2);
|
||||
long y = js_tonumber(J, 3);
|
||||
js_pushboolean(J, os_call_ext_str_3("GrPlot", dc, x, y));
|
||||
}
|
||||
|
||||
void jsLT_initgr(js_State *J)
|
||||
{
|
||||
js_pushobject(J, jsV_newobject(J, -1, J->Object_prototype));
|
||||
{
|
||||
jsB_propf(J, "Gr.blot", Gr_blot, 4);
|
||||
jsB_propf(J, "Gr.peek", Gr_peek, 3);
|
||||
jsB_propf(J, "Gr.plot", Gr_plot, 3);
|
||||
}
|
||||
js_defglobal(J, "Gr", JS_DONTENUM);
|
||||
}
|
||||
|
||||
/** OS **/
|
||||
|
||||
// U0 Beep(I8 ona=62,Bool busy=FALSE)
|
||||
void OS_beep(js_State *J)
|
||||
{
|
||||
long ona = value_or_number(62, 1);
|
||||
long busy = null_or_number(2);
|
||||
os_call_ext_str_2("Beep", ona, busy);
|
||||
js_pushundefined(J);
|
||||
}
|
||||
|
||||
// _extern _MALLOC U8 *MAlloc(I64 size,CTask *mem_task=NULL);
|
||||
void OS_malloc(js_State *J)
|
||||
{
|
||||
if (js_isundefined(J, 1)) {
|
||||
js_pushundefined(J);
|
||||
return;
|
||||
}
|
||||
long size = js_tonumber(J, 1);
|
||||
long mem_task = null_or_number(2);
|
||||
js_pushnumber(J, os_call_ext_str_2("MAlloc", size, mem_task));
|
||||
}
|
||||
|
||||
// U8 *CAlloc(I64 size,CTask *mem_task=NULL)
|
||||
void OS_calloc(js_State *J)
|
||||
{
|
||||
if (js_isundefined(J, 1)) {
|
||||
js_pushundefined(J);
|
||||
return;
|
||||
}
|
||||
long size = js_tonumber(J, 1);
|
||||
long mem_task = null_or_number(2);
|
||||
js_pushnumber(J, os_call_ext_str_2("CAlloc", size, mem_task));
|
||||
}
|
||||
|
||||
// _extern _FREE U0 Free(U8 *addr); //Free MAlloc()ed memory chunk.
|
||||
void OS_free(js_State *J)
|
||||
{
|
||||
if (!js_isundefined(J, 1)) {
|
||||
long addr = js_tonumber(J, 1);
|
||||
os_call_ext_str_1("Free", addr);
|
||||
}
|
||||
js_pushundefined(J);
|
||||
}
|
||||
|
||||
void OS_jiffies(js_State *J)
|
||||
{
|
||||
js_pushnumber(J, os_jiffies());
|
||||
}
|
||||
|
||||
void OS_read_file_as_string(js_State *J)
|
||||
{
|
||||
if (js_isundefined(J, 1)) {
|
||||
js_pushundefined(J);
|
||||
return;
|
||||
}
|
||||
const char* filename = js_tostring(J, 1);
|
||||
long file_as_string = os_call_ext_str_3("FileRead", (uint64_t)filename, 0, 0);
|
||||
js_pushstring(J, file_as_string ? (char*)file_as_string : "");
|
||||
}
|
||||
|
||||
// U0 Reboot()
|
||||
void OS_reboot(js_State *J)
|
||||
{
|
||||
os_call_ext_str("Reboot");
|
||||
}
|
||||
|
||||
// U0 Sleep(I64 mS)
|
||||
void OS_sleep(js_State *J)
|
||||
{
|
||||
if (!js_isundefined(J, 1)) {
|
||||
long ms = js_tonumber(J, 1);
|
||||
os_call_ext_str_1("Sleep", ms);
|
||||
}
|
||||
js_pushundefined(J);
|
||||
}
|
||||
|
||||
// U0 Snd(I8 ona=0)
|
||||
void OS_snd(js_State *J)
|
||||
{
|
||||
long ona = null_or_number(1);
|
||||
os_call_ext_str_1("Snd", ona);
|
||||
js_pushundefined(J);
|
||||
}
|
||||
|
||||
void OS_write_file_as_string(js_State *J)
|
||||
{
|
||||
if (js_isundefined(J, 1) || js_isundefined(J, 2)) {
|
||||
js_pushundefined(J);
|
||||
return;
|
||||
}
|
||||
const char* filename = js_tostring(J, 1);
|
||||
const char* string = js_tostring(J, 2);
|
||||
long length = strlen(string);
|
||||
js_pushnumber(J, os_call_ext_str_5("FileWrite", (uint64_t)filename, (uint64_t)string, length, 0, 0));
|
||||
}
|
||||
|
||||
void jsLT_initos(js_State *J)
|
||||
{
|
||||
js_pushobject(J, jsV_newobject(J, -1, J->Object_prototype));
|
||||
{
|
||||
jsB_propf(J, "OS.beep", OS_beep, 2);
|
||||
jsB_propf(J, "OS.malloc", OS_malloc, 2);
|
||||
jsB_propf(J, "OS.calloc", OS_calloc, 2);
|
||||
jsB_propf(J, "OS.free", OS_free, 1);
|
||||
jsB_propf(J, "OS.jiffies", OS_jiffies, 0);
|
||||
jsB_propf(J, "OS.read_file_as_string", OS_read_file_as_string, 1);
|
||||
jsB_propf(J, "OS.write_file_as_string", OS_write_file_as_string, 2);
|
||||
jsB_propf(J, "OS.reboot", OS_reboot, 0);
|
||||
jsB_propf(J, "OS.sleep", OS_sleep, 1);
|
||||
jsB_propf(J, "OS.snd", OS_snd, 1);
|
||||
}
|
||||
js_defglobal(J, "OS", JS_DONTENUM);
|
||||
}
|
||||
|
||||
void js_initlibtemple(js_State *J)
|
||||
{
|
||||
jsLT_initdc(J);
|
||||
jsLT_initgr(J);
|
||||
jsLT_initos(J);
|
||||
}
|
194
src/mujs/jsmath.c
Normal file
194
src/mujs/jsmath.c
Normal file
|
@ -0,0 +1,194 @@
|
|||
#include "jsi.h"
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1700) /* VS2012 has stdint.h */
|
||||
typedef unsigned int uint32_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#include <time.h>
|
||||
|
||||
static double jsM_round(double x)
|
||||
{
|
||||
if (isnan(x)) return x;
|
||||
if (isinf(x)) return x;
|
||||
if (x == 0) return x;
|
||||
if (x > 0 && x < 0.5) return 0;
|
||||
if (x < 0 && x >= -0.5) return -0;
|
||||
return floor(x + 0.5);
|
||||
}
|
||||
|
||||
static void Math_abs(js_State *J)
|
||||
{
|
||||
js_pushnumber(J, fabs(js_tonumber(J, 1)));
|
||||
}
|
||||
|
||||
static void Math_acos(js_State *J)
|
||||
{
|
||||
js_pushnumber(J, acos(js_tonumber(J, 1)));
|
||||
}
|
||||
|
||||
static void Math_asin(js_State *J)
|
||||
{
|
||||
js_pushnumber(J, asin(js_tonumber(J, 1)));
|
||||
}
|
||||
|
||||
static void Math_atan(js_State *J)
|
||||
{
|
||||
js_pushnumber(J, atan(js_tonumber(J, 1)));
|
||||
}
|
||||
|
||||
static void Math_atan2(js_State *J)
|
||||
{
|
||||
double y = js_tonumber(J, 1);
|
||||
double x = js_tonumber(J, 2);
|
||||
js_pushnumber(J, atan2(y, x));
|
||||
}
|
||||
|
||||
static void Math_ceil(js_State *J)
|
||||
{
|
||||
js_pushnumber(J, ceil(js_tonumber(J, 1)));
|
||||
}
|
||||
|
||||
static void Math_cos(js_State *J)
|
||||
{
|
||||
js_pushnumber(J, cos(js_tonumber(J, 1)));
|
||||
}
|
||||
|
||||
static void Math_exp(js_State *J)
|
||||
{
|
||||
js_pushnumber(J, exp(js_tonumber(J, 1)));
|
||||
}
|
||||
|
||||
static void Math_floor(js_State *J)
|
||||
{
|
||||
js_pushnumber(J, floor(js_tonumber(J, 1)));
|
||||
}
|
||||
|
||||
static void Math_log(js_State *J)
|
||||
{
|
||||
js_pushnumber(J, log(js_tonumber(J, 1)));
|
||||
}
|
||||
|
||||
static void Math_pow(js_State *J)
|
||||
{
|
||||
double x = js_tonumber(J, 1);
|
||||
double y = js_tonumber(J, 2);
|
||||
if (!isfinite(y) && fabs(x) == 1)
|
||||
js_pushnumber(J, NAN);
|
||||
else
|
||||
js_pushnumber(J, pow(x,y));
|
||||
}
|
||||
|
||||
static void Math_random(js_State *J)
|
||||
{
|
||||
/* Lehmer generator with a=48271 and m=2^31-1 */
|
||||
/* Park & Miller (1988). Random Number Generators: Good ones are hard to find. */
|
||||
J->seed = (uint64_t) J->seed * 48271 % 0x7fffffff;
|
||||
js_pushnumber(J, (double) J->seed / 0x7fffffff);
|
||||
}
|
||||
|
||||
static void Math_init_random(js_State *J)
|
||||
{
|
||||
/* Pick initial seed by scrambling current time with Xorshift. */
|
||||
/* Marsaglia (2003). Xorshift RNGs. */
|
||||
J->seed = time(0) + 123;
|
||||
J->seed ^= J->seed << 13;
|
||||
J->seed ^= J->seed >> 17;
|
||||
J->seed ^= J->seed << 5;
|
||||
J->seed %= 0x7fffffff;
|
||||
}
|
||||
|
||||
static void Math_round(js_State *J)
|
||||
{
|
||||
double x = js_tonumber(J, 1);
|
||||
js_pushnumber(J, jsM_round(x));
|
||||
}
|
||||
|
||||
static void Math_sin(js_State *J)
|
||||
{
|
||||
js_pushnumber(J, sin(js_tonumber(J, 1)));
|
||||
}
|
||||
|
||||
static void Math_sqrt(js_State *J)
|
||||
{
|
||||
js_pushnumber(J, sqrt(js_tonumber(J, 1)));
|
||||
}
|
||||
|
||||
static void Math_tan(js_State *J)
|
||||
{
|
||||
js_pushnumber(J, tan(js_tonumber(J, 1)));
|
||||
}
|
||||
|
||||
static void Math_max(js_State *J)
|
||||
{
|
||||
int i, n = js_gettop(J);
|
||||
double x = -INFINITY;
|
||||
for (i = 1; i < n; ++i) {
|
||||
double y = js_tonumber(J, i);
|
||||
if (isnan(y)) {
|
||||
x = y;
|
||||
break;
|
||||
}
|
||||
if (signbit(x) == signbit(y))
|
||||
x = x > y ? x : y;
|
||||
else if (signbit(x))
|
||||
x = y;
|
||||
}
|
||||
js_pushnumber(J, x);
|
||||
}
|
||||
|
||||
static void Math_min(js_State *J)
|
||||
{
|
||||
int i, n = js_gettop(J);
|
||||
double x = INFINITY;
|
||||
for (i = 1; i < n; ++i) {
|
||||
double y = js_tonumber(J, i);
|
||||
if (isnan(y)) {
|
||||
x = y;
|
||||
break;
|
||||
}
|
||||
if (signbit(x) == signbit(y))
|
||||
x = x < y ? x : y;
|
||||
else if (signbit(y))
|
||||
x = y;
|
||||
}
|
||||
js_pushnumber(J, x);
|
||||
}
|
||||
|
||||
void jsB_initmath(js_State *J)
|
||||
{
|
||||
Math_init_random(J);
|
||||
js_pushobject(J, jsV_newobject(J, JS_CMATH, J->Object_prototype));
|
||||
{
|
||||
jsB_propn(J, "E", 2.7182818284590452354);
|
||||
jsB_propn(J, "LN10", 2.302585092994046);
|
||||
jsB_propn(J, "LN2", 0.6931471805599453);
|
||||
jsB_propn(J, "LOG2E", 1.4426950408889634);
|
||||
jsB_propn(J, "LOG10E", 0.4342944819032518);
|
||||
jsB_propn(J, "PI", 3.1415926535897932);
|
||||
jsB_propn(J, "SQRT1_2", 0.7071067811865476);
|
||||
jsB_propn(J, "SQRT2", 1.4142135623730951);
|
||||
|
||||
jsB_propf(J, "Math.abs", Math_abs, 1);
|
||||
jsB_propf(J, "Math.acos", Math_acos, 1);
|
||||
jsB_propf(J, "Math.asin", Math_asin, 1);
|
||||
jsB_propf(J, "Math.atan", Math_atan, 1);
|
||||
jsB_propf(J, "Math.atan2", Math_atan2, 2);
|
||||
jsB_propf(J, "Math.ceil", Math_ceil, 1);
|
||||
jsB_propf(J, "Math.cos", Math_cos, 1);
|
||||
jsB_propf(J, "Math.exp", Math_exp, 1);
|
||||
jsB_propf(J, "Math.floor", Math_floor, 1);
|
||||
jsB_propf(J, "Math.log", Math_log, 1);
|
||||
jsB_propf(J, "Math.max", Math_max, 0); /* 2 */
|
||||
jsB_propf(J, "Math.min", Math_min, 0); /* 2 */
|
||||
jsB_propf(J, "Math.pow", Math_pow, 2);
|
||||
jsB_propf(J, "Math.random", Math_random, 0);
|
||||
jsB_propf(J, "Math.round", Math_round, 1);
|
||||
jsB_propf(J, "Math.sin", Math_sin, 1);
|
||||
jsB_propf(J, "Math.sqrt", Math_sqrt, 1);
|
||||
jsB_propf(J, "Math.tan", Math_tan, 1);
|
||||
}
|
||||
js_defglobal(J, "Math", JS_DONTENUM);
|
||||
}
|
198
src/mujs/jsnumber.c
Normal file
198
src/mujs/jsnumber.c
Normal file
|
@ -0,0 +1,198 @@
|
|||
#include "jsi.h"
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1700) /* VS2012 has stdint.h */
|
||||
typedef unsigned __int64 uint64_t;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
static void jsB_new_Number(js_State *J)
|
||||
{
|
||||
js_newnumber(J, js_gettop(J) > 1 ? js_tonumber(J, 1) : 0);
|
||||
}
|
||||
|
||||
static void jsB_Number(js_State *J)
|
||||
{
|
||||
js_pushnumber(J, js_gettop(J) > 1 ? js_tonumber(J, 1) : 0);
|
||||
}
|
||||
|
||||
static void Np_valueOf(js_State *J)
|
||||
{
|
||||
js_Object *self = js_toobject(J, 0);
|
||||
if (self->type != JS_CNUMBER) js_typeerror(J, "not a number");
|
||||
js_pushnumber(J, self->u.number);
|
||||
}
|
||||
|
||||
static void Np_toString(js_State *J)
|
||||
{
|
||||
char buf[100];
|
||||
js_Object *self = js_toobject(J, 0);
|
||||
int radix = js_isundefined(J, 1) ? 10 : js_tointeger(J, 1);
|
||||
double x = 0;
|
||||
if (self->type != JS_CNUMBER)
|
||||
js_typeerror(J, "not a number");
|
||||
x = self->u.number;
|
||||
if (radix == 10) {
|
||||
js_pushstring(J, jsV_numbertostring(J, buf, x));
|
||||
return;
|
||||
}
|
||||
if (radix < 2 || radix > 36)
|
||||
js_rangeerror(J, "invalid radix");
|
||||
|
||||
/* lame number to string conversion for any radix from 2 to 36 */
|
||||
{
|
||||
static const char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
|
||||
double number = x;
|
||||
int sign = x < 0;
|
||||
js_Buffer *sb = NULL;
|
||||
uint64_t u, limit = ((uint64_t)1<<52);
|
||||
|
||||
int ndigits, exp, point;
|
||||
|
||||
if (number == 0) { js_pushstring(J, "0"); return; }
|
||||
if (isnan(number)) { js_pushstring(J, "NaN"); return; }
|
||||
if (isinf(number)) { js_pushstring(J, sign ? "-Infinity" : "Infinity"); return; }
|
||||
|
||||
if (sign)
|
||||
number = -number;
|
||||
|
||||
/* fit as many digits as we want in an int */
|
||||
exp = 0;
|
||||
while (number * pow(radix, exp) > limit)
|
||||
--exp;
|
||||
while (number * pow(radix, exp+1) < limit)
|
||||
++exp;
|
||||
u = number * pow(radix, exp) + 0.5;
|
||||
|
||||
/* trim trailing zeros */
|
||||
while (u > 0 && (u % radix) == 0) {
|
||||
u /= radix;
|
||||
--exp;
|
||||
}
|
||||
|
||||
/* serialize digits */
|
||||
ndigits = 0;
|
||||
while (u > 0) {
|
||||
buf[ndigits++] = digits[u % radix];
|
||||
u /= radix;
|
||||
}
|
||||
point = ndigits - exp;
|
||||
|
||||
if (js_try(J)) {
|
||||
js_free(J, sb);
|
||||
js_throw(J);
|
||||
}
|
||||
|
||||
if (sign)
|
||||
js_putc(J, &sb, '-');
|
||||
|
||||
if (point <= 0) {
|
||||
js_putc(J, &sb, '0');
|
||||
js_putc(J, &sb, '.');
|
||||
while (point++ < 0)
|
||||
js_putc(J, &sb, '0');
|
||||
while (ndigits-- > 0)
|
||||
js_putc(J, &sb, buf[ndigits]);
|
||||
} else {
|
||||
while (ndigits-- > 0) {
|
||||
js_putc(J, &sb, buf[ndigits]);
|
||||
if (--point == 0 && ndigits > 0)
|
||||
js_putc(J, &sb, '.');
|
||||
}
|
||||
while (point-- > 0)
|
||||
js_putc(J, &sb, '0');
|
||||
}
|
||||
|
||||
js_putc(J, &sb, 0);
|
||||
js_pushstring(J, sb->s);
|
||||
|
||||
js_endtry(J);
|
||||
js_free(J, sb);
|
||||
}
|
||||
}
|
||||
|
||||
/* Customized ToString() on a number */
|
||||
static void numtostr(js_State *J, const char *fmt, int w, double n)
|
||||
{
|
||||
/* buf needs to fit printf("%.20f", 1e20) */
|
||||
char buf[50], *e;
|
||||
sprintf(buf, fmt, w, n);
|
||||
e = strchr(buf, 'e');
|
||||
if (e) {
|
||||
int exp = atoi(e+1);
|
||||
sprintf(e, "e%+d", exp);
|
||||
}
|
||||
js_pushstring(J, buf);
|
||||
}
|
||||
|
||||
static void Np_toFixed(js_State *J)
|
||||
{
|
||||
js_Object *self = js_toobject(J, 0);
|
||||
int width = js_tointeger(J, 1);
|
||||
char buf[32];
|
||||
double x;
|
||||
if (self->type != JS_CNUMBER) js_typeerror(J, "not a number");
|
||||
if (width < 0) js_rangeerror(J, "precision %d out of range", width);
|
||||
if (width > 20) js_rangeerror(J, "precision %d out of range", width);
|
||||
x = self->u.number;
|
||||
if (isnan(x) || isinf(x) || x <= -1e21 || x >= 1e21)
|
||||
js_pushstring(J, jsV_numbertostring(J, buf, x));
|
||||
else
|
||||
numtostr(J, "%.*f", width, x);
|
||||
}
|
||||
|
||||
static void Np_toExponential(js_State *J)
|
||||
{
|
||||
js_Object *self = js_toobject(J, 0);
|
||||
int width = js_tointeger(J, 1);
|
||||
char buf[32];
|
||||
double x;
|
||||
if (self->type != JS_CNUMBER) js_typeerror(J, "not a number");
|
||||
if (width < 0) js_rangeerror(J, "precision %d out of range", width);
|
||||
if (width > 20) js_rangeerror(J, "precision %d out of range", width);
|
||||
x = self->u.number;
|
||||
if (isnan(x) || isinf(x))
|
||||
js_pushstring(J, jsV_numbertostring(J, buf, x));
|
||||
else
|
||||
numtostr(J, "%.*e", width, x);
|
||||
}
|
||||
|
||||
static void Np_toPrecision(js_State *J)
|
||||
{
|
||||
js_Object *self = js_toobject(J, 0);
|
||||
int width = js_tointeger(J, 1);
|
||||
char buf[32];
|
||||
double x;
|
||||
if (self->type != JS_CNUMBER) js_typeerror(J, "not a number");
|
||||
if (width < 1) js_rangeerror(J, "precision %d out of range", width);
|
||||
if (width > 21) js_rangeerror(J, "precision %d out of range", width);
|
||||
x = self->u.number;
|
||||
if (isnan(x) || isinf(x))
|
||||
js_pushstring(J, jsV_numbertostring(J, buf, x));
|
||||
else
|
||||
numtostr(J, "%.*g", width, x);
|
||||
}
|
||||
|
||||
void jsB_initnumber(js_State *J)
|
||||
{
|
||||
J->Number_prototype->u.number = 0;
|
||||
|
||||
js_pushobject(J, J->Number_prototype);
|
||||
{
|
||||
jsB_propf(J, "Number.prototype.valueOf", Np_valueOf, 0);
|
||||
jsB_propf(J, "Number.prototype.toString", Np_toString, 1);
|
||||
jsB_propf(J, "Number.prototype.toLocaleString", Np_toString, 0);
|
||||
jsB_propf(J, "Number.prototype.toFixed", Np_toFixed, 1);
|
||||
jsB_propf(J, "Number.prototype.toExponential", Np_toExponential, 1);
|
||||
jsB_propf(J, "Number.prototype.toPrecision", Np_toPrecision, 1);
|
||||
}
|
||||
js_newcconstructor(J, jsB_Number, jsB_new_Number, "Number", 0); /* 1 */
|
||||
{
|
||||
jsB_propn(J, "MAX_VALUE", 1.7976931348623157e+308);
|
||||
jsB_propn(J, "MIN_VALUE", 5e-324);
|
||||
jsB_propn(J, "NaN", NAN);
|
||||
jsB_propn(J, "NEGATIVE_INFINITY", -INFINITY);
|
||||
jsB_propn(J, "POSITIVE_INFINITY", INFINITY);
|
||||
}
|
||||
js_defglobal(J, "Number", JS_DONTENUM);
|
||||
}
|
560
src/mujs/jsobject.c
Normal file
560
src/mujs/jsobject.c
Normal file
|
@ -0,0 +1,560 @@
|
|||
#include "jsi.h"
|
||||
|
||||
static void jsB_new_Object(js_State *J)
|
||||
{
|
||||
if (js_isundefined(J, 1) || js_isnull(J, 1))
|
||||
js_newobject(J);
|
||||
else
|
||||
js_pushobject(J, js_toobject(J, 1));
|
||||
}
|
||||
|
||||
static void jsB_Object(js_State *J)
|
||||
{
|
||||
if (js_isundefined(J, 1) || js_isnull(J, 1))
|
||||
js_newobject(J);
|
||||
else
|
||||
js_pushobject(J, js_toobject(J, 1));
|
||||
}
|
||||
|
||||
static void Op_toString(js_State *J)
|
||||
{
|
||||
if (js_isundefined(J, 0))
|
||||
js_pushliteral(J, "[object Undefined]");
|
||||
else if (js_isnull(J, 0))
|
||||
js_pushliteral(J, "[object Null]");
|
||||
else {
|
||||
js_Object *self = js_toobject(J, 0);
|
||||
switch (self->type) {
|
||||
case JS_COBJECT: js_pushliteral(J, "[object Object]"); break;
|
||||
case JS_CARRAY: js_pushliteral(J, "[object Array]"); break;
|
||||
case JS_CFUNCTION: js_pushliteral(J, "[object Function]"); break;
|
||||
case JS_CSCRIPT: js_pushliteral(J, "[object Function]"); break;
|
||||
case JS_CCFUNCTION: js_pushliteral(J, "[object Function]"); break;
|
||||
case JS_CERROR: js_pushliteral(J, "[object Error]"); break;
|
||||
case JS_CBOOLEAN: js_pushliteral(J, "[object Boolean]"); break;
|
||||
case JS_CNUMBER: js_pushliteral(J, "[object Number]"); break;
|
||||
case JS_CSTRING: js_pushliteral(J, "[object String]"); break;
|
||||
case JS_CREGEXP: js_pushliteral(J, "[object RegExp]"); break;
|
||||
case JS_CDATE: js_pushliteral(J, "[object Date]"); break;
|
||||
case JS_CMATH: js_pushliteral(J, "[object Math]"); break;
|
||||
case JS_CJSON: js_pushliteral(J, "[object JSON]"); break;
|
||||
case JS_CARGUMENTS: js_pushliteral(J, "[object Arguments]"); break;
|
||||
case JS_CITERATOR: js_pushliteral(J, "[object Iterator]"); break;
|
||||
case JS_CUSERDATA:
|
||||
js_pushliteral(J, "[object ");
|
||||
js_pushliteral(J, self->u.user.tag);
|
||||
js_concat(J);
|
||||
js_pushliteral(J, "]");
|
||||
js_concat(J);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Op_valueOf(js_State *J)
|
||||
{
|
||||
js_copy(J, 0);
|
||||
}
|
||||
|
||||
static void Op_hasOwnProperty(js_State *J)
|
||||
{
|
||||
js_Object *self = js_toobject(J, 0);
|
||||
const char *name = js_tostring(J, 1);
|
||||
js_Property *ref;
|
||||
int k;
|
||||
|
||||
if (self->type == JS_CSTRING) {
|
||||
if (js_isarrayindex(J, name, &k) && k >= 0 && k < self->u.s.length) {
|
||||
js_pushboolean(J, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (self->type == JS_CARRAY && self->u.a.simple) {
|
||||
if (js_isarrayindex(J, name, &k) && k >= 0 && k < self->u.a.flat_length) {
|
||||
js_pushboolean(J, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ref = jsV_getownproperty(J, self, name);
|
||||
js_pushboolean(J, ref != NULL);
|
||||
}
|
||||
|
||||
static void Op_isPrototypeOf(js_State *J)
|
||||
{
|
||||
js_Object *self = js_toobject(J, 0);
|
||||
if (js_isobject(J, 1)) {
|
||||
js_Object *V = js_toobject(J, 1);
|
||||
do {
|
||||
V = V->prototype;
|
||||
if (V == self) {
|
||||
js_pushboolean(J, 1);
|
||||
return;
|
||||
}
|
||||
} while (V);
|
||||
}
|
||||
js_pushboolean(J, 0);
|
||||
}
|
||||
|
||||
static void Op_propertyIsEnumerable(js_State *J)
|
||||
{
|
||||
js_Object *self = js_toobject(J, 0);
|
||||
const char *name = js_tostring(J, 1);
|
||||
js_Property *ref = jsV_getownproperty(J, self, name);
|
||||
js_pushboolean(J, ref && !(ref->atts & JS_DONTENUM));
|
||||
}
|
||||
|
||||
static void O_getPrototypeOf(js_State *J)
|
||||
{
|
||||
js_Object *obj;
|
||||
if (!js_isobject(J, 1))
|
||||
js_typeerror(J, "not an object");
|
||||
obj = js_toobject(J, 1);
|
||||
if (obj->prototype)
|
||||
js_pushobject(J, obj->prototype);
|
||||
else
|
||||
js_pushnull(J);
|
||||
}
|
||||
|
||||
static void O_getOwnPropertyDescriptor(js_State *J)
|
||||
{
|
||||
js_Object *obj;
|
||||
js_Property *ref;
|
||||
if (!js_isobject(J, 1))
|
||||
js_typeerror(J, "not an object");
|
||||
obj = js_toobject(J, 1);
|
||||
ref = jsV_getproperty(J, obj, js_tostring(J, 2));
|
||||
if (!ref) {
|
||||
/* TODO: builtin properties (string and array index and length, regexp flags, etc) */
|
||||
js_pushundefined(J);
|
||||
} else {
|
||||
js_newobject(J);
|
||||
if (!ref->getter && !ref->setter) {
|
||||
js_pushvalue(J, ref->value);
|
||||
js_defproperty(J, -2, "value", 0);
|
||||
js_pushboolean(J, !(ref->atts & JS_READONLY));
|
||||
js_defproperty(J, -2, "writable", 0);
|
||||
} else {
|
||||
if (ref->getter)
|
||||
js_pushobject(J, ref->getter);
|
||||
else
|
||||
js_pushundefined(J);
|
||||
js_defproperty(J, -2, "get", 0);
|
||||
if (ref->setter)
|
||||
js_pushobject(J, ref->setter);
|
||||
else
|
||||
js_pushundefined(J);
|
||||
js_defproperty(J, -2, "set", 0);
|
||||
}
|
||||
js_pushboolean(J, !(ref->atts & JS_DONTENUM));
|
||||
js_defproperty(J, -2, "enumerable", 0);
|
||||
js_pushboolean(J, !(ref->atts & JS_DONTCONF));
|
||||
js_defproperty(J, -2, "configurable", 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int O_getOwnPropertyNames_walk(js_State *J, js_Property *ref, int i)
|
||||
{
|
||||
if (ref->left->level)
|
||||
i = O_getOwnPropertyNames_walk(J, ref->left, i);
|
||||
js_pushstring(J, ref->name);
|
||||
js_setindex(J, -2, i++);
|
||||
if (ref->right->level)
|
||||
i = O_getOwnPropertyNames_walk(J, ref->right, i);
|
||||
return i;
|
||||
}
|
||||
|
||||
static void O_getOwnPropertyNames(js_State *J)
|
||||
{
|
||||
js_Object *obj;
|
||||
char name[32];
|
||||
int k;
|
||||
int i;
|
||||
|
||||
if (!js_isobject(J, 1))
|
||||
js_typeerror(J, "not an object");
|
||||
obj = js_toobject(J, 1);
|
||||
|
||||
js_newarray(J);
|
||||
|
||||
if (obj->properties->level)
|
||||
i = O_getOwnPropertyNames_walk(J, obj->properties, 0);
|
||||
else
|
||||
i = 0;
|
||||
|
||||
if (obj->type == JS_CARRAY) {
|
||||
js_pushliteral(J, "length");
|
||||
js_setindex(J, -2, i++);
|
||||
if (obj->u.a.simple) {
|
||||
for (k = 0; k < obj->u.a.flat_length; ++k) {
|
||||
js_itoa(name, k);
|
||||
js_pushstring(J, name);
|
||||
js_setindex(J, -2, i++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (obj->type == JS_CSTRING) {
|
||||
js_pushliteral(J, "length");
|
||||
js_setindex(J, -2, i++);
|
||||
for (k = 0; k < obj->u.s.length; ++k) {
|
||||
js_itoa(name, k);
|
||||
js_pushstring(J, name);
|
||||
js_setindex(J, -2, i++);
|
||||
}
|
||||
}
|
||||
|
||||
if (obj->type == JS_CREGEXP) {
|
||||
js_pushliteral(J, "source");
|
||||
js_setindex(J, -2, i++);
|
||||
js_pushliteral(J, "global");
|
||||
js_setindex(J, -2, i++);
|
||||
js_pushliteral(J, "ignoreCase");
|
||||
js_setindex(J, -2, i++);
|
||||
js_pushliteral(J, "multiline");
|
||||
js_setindex(J, -2, i++);
|
||||
js_pushliteral(J, "lastIndex");
|
||||
js_setindex(J, -2, i++);
|
||||
}
|
||||
}
|
||||
|
||||
static void ToPropertyDescriptor(js_State *J, js_Object *obj, const char *name, js_Object *desc)
|
||||
{
|
||||
int haswritable = 0;
|
||||
int hasvalue = 0;
|
||||
int enumerable = 0;
|
||||
int configurable = 0;
|
||||
int writable = 0;
|
||||
int atts = 0;
|
||||
|
||||
js_pushobject(J, obj);
|
||||
js_pushobject(J, desc);
|
||||
|
||||
if (js_hasproperty(J, -1, "writable")) {
|
||||
haswritable = 1;
|
||||
writable = js_toboolean(J, -1);
|
||||
js_pop(J, 1);
|
||||
}
|
||||
if (js_hasproperty(J, -1, "enumerable")) {
|
||||
enumerable = js_toboolean(J, -1);
|
||||
js_pop(J, 1);
|
||||
}
|
||||
if (js_hasproperty(J, -1, "configurable")) {
|
||||
configurable = js_toboolean(J, -1);
|
||||
js_pop(J, 1);
|
||||
}
|
||||
if (js_hasproperty(J, -1, "value")) {
|
||||
hasvalue = 1;
|
||||
js_defproperty(J, -3, name, 0);
|
||||
}
|
||||
|
||||
if (!writable) atts |= JS_READONLY;
|
||||
if (!enumerable) atts |= JS_DONTENUM;
|
||||
if (!configurable) atts |= JS_DONTCONF;
|
||||
|
||||
if (js_hasproperty(J, -1, "get")) {
|
||||
if (haswritable || hasvalue)
|
||||
js_typeerror(J, "value/writable and get/set attributes are exclusive");
|
||||
} else {
|
||||
js_pushundefined(J);
|
||||
}
|
||||
|
||||
if (js_hasproperty(J, -2, "set")) {
|
||||
if (haswritable || hasvalue)
|
||||
js_typeerror(J, "value/writable and get/set attributes are exclusive");
|
||||
} else {
|
||||
js_pushundefined(J);
|
||||
}
|
||||
|
||||
js_defaccessor(J, -4, name, atts);
|
||||
|
||||
js_pop(J, 2);
|
||||
}
|
||||
|
||||
static void O_defineProperty(js_State *J)
|
||||
{
|
||||
if (!js_isobject(J, 1)) js_typeerror(J, "not an object");
|
||||
if (!js_isobject(J, 3)) js_typeerror(J, "not an object");
|
||||
ToPropertyDescriptor(J, js_toobject(J, 1), js_tostring(J, 2), js_toobject(J, 3));
|
||||
js_copy(J, 1);
|
||||
}
|
||||
|
||||
static void O_defineProperties_walk(js_State *J, js_Property *ref)
|
||||
{
|
||||
if (ref->left->level)
|
||||
O_defineProperties_walk(J, ref->left);
|
||||
if (!(ref->atts & JS_DONTENUM)) {
|
||||
js_pushvalue(J, ref->value);
|
||||
ToPropertyDescriptor(J, js_toobject(J, 1), ref->name, js_toobject(J, -1));
|
||||
js_pop(J, 1);
|
||||
}
|
||||
if (ref->right->level)
|
||||
O_defineProperties_walk(J, ref->right);
|
||||
}
|
||||
|
||||
static void O_defineProperties(js_State *J)
|
||||
{
|
||||
js_Object *props;
|
||||
|
||||
if (!js_isobject(J, 1)) js_typeerror(J, "not an object");
|
||||
if (!js_isobject(J, 2)) js_typeerror(J, "not an object");
|
||||
|
||||
props = js_toobject(J, 2);
|
||||
if (props->properties->level)
|
||||
O_defineProperties_walk(J, props->properties);
|
||||
|
||||
js_copy(J, 1);
|
||||
}
|
||||
|
||||
static void O_create_walk(js_State *J, js_Object *obj, js_Property *ref)
|
||||
{
|
||||
if (ref->left->level)
|
||||
O_create_walk(J, obj, ref->left);
|
||||
if (!(ref->atts & JS_DONTENUM)) {
|
||||
if (ref->value.t.type != JS_TOBJECT)
|
||||
js_typeerror(J, "not an object");
|
||||
ToPropertyDescriptor(J, obj, ref->name, ref->value.u.object);
|
||||
}
|
||||
if (ref->right->level)
|
||||
O_create_walk(J, obj, ref->right);
|
||||
}
|
||||
|
||||
static void O_create(js_State *J)
|
||||
{
|
||||
js_Object *obj;
|
||||
js_Object *proto;
|
||||
js_Object *props;
|
||||
|
||||
if (js_isobject(J, 1))
|
||||
proto = js_toobject(J, 1);
|
||||
else if (js_isnull(J, 1))
|
||||
proto = NULL;
|
||||
else
|
||||
js_typeerror(J, "not an object or null");
|
||||
|
||||
obj = jsV_newobject(J, JS_COBJECT, proto);
|
||||
js_pushobject(J, obj);
|
||||
|
||||
if (js_isdefined(J, 2)) {
|
||||
if (!js_isobject(J, 2))
|
||||
js_typeerror(J, "not an object");
|
||||
props = js_toobject(J, 2);
|
||||
if (props->properties->level)
|
||||
O_create_walk(J, obj, props->properties);
|
||||
}
|
||||
}
|
||||
|
||||
static int O_keys_walk(js_State *J, js_Property *ref, int i)
|
||||
{
|
||||
if (ref->left->level)
|
||||
i = O_keys_walk(J, ref->left, i);
|
||||
if (!(ref->atts & JS_DONTENUM)) {
|
||||
js_pushstring(J, ref->name);
|
||||
js_setindex(J, -2, i++);
|
||||
}
|
||||
if (ref->right->level)
|
||||
i = O_keys_walk(J, ref->right, i);
|
||||
return i;
|
||||
}
|
||||
|
||||
static void O_keys(js_State *J)
|
||||
{
|
||||
js_Object *obj;
|
||||
char name[32];
|
||||
int i, k;
|
||||
|
||||
if (!js_isobject(J, 1))
|
||||
js_typeerror(J, "not an object");
|
||||
obj = js_toobject(J, 1);
|
||||
|
||||
js_newarray(J);
|
||||
|
||||
if (obj->properties->level)
|
||||
i = O_keys_walk(J, obj->properties, 0);
|
||||
else
|
||||
i = 0;
|
||||
|
||||
if (obj->type == JS_CSTRING) {
|
||||
for (k = 0; k < obj->u.s.length; ++k) {
|
||||
js_itoa(name, k);
|
||||
js_pushstring(J, name);
|
||||
js_setindex(J, -2, i++);
|
||||
}
|
||||
}
|
||||
|
||||
if (obj->type == JS_CARRAY && obj->u.a.simple) {
|
||||
for (k = 0; k < obj->u.a.flat_length; ++k) {
|
||||
js_itoa(name, k);
|
||||
js_pushstring(J, name);
|
||||
js_setindex(J, -2, i++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void O_preventExtensions(js_State *J)
|
||||
{
|
||||
js_Object *obj;
|
||||
if (!js_isobject(J, 1))
|
||||
js_typeerror(J, "not an object");
|
||||
obj = js_toobject(J, 1);
|
||||
jsR_unflattenarray(J, obj);
|
||||
obj->extensible = 0;
|
||||
js_copy(J, 1);
|
||||
}
|
||||
|
||||
static void O_isExtensible(js_State *J)
|
||||
{
|
||||
if (!js_isobject(J, 1))
|
||||
js_typeerror(J, "not an object");
|
||||
js_pushboolean(J, js_toobject(J, 1)->extensible);
|
||||
}
|
||||
|
||||
static void O_seal_walk(js_State *J, js_Property *ref)
|
||||
{
|
||||
if (ref->left->level)
|
||||
O_seal_walk(J, ref->left);
|
||||
ref->atts |= JS_DONTCONF;
|
||||
if (ref->right->level)
|
||||
O_seal_walk(J, ref->right);
|
||||
}
|
||||
|
||||
static void O_seal(js_State *J)
|
||||
{
|
||||
js_Object *obj;
|
||||
|
||||
if (!js_isobject(J, 1))
|
||||
js_typeerror(J, "not an object");
|
||||
|
||||
obj = js_toobject(J, 1);
|
||||
jsR_unflattenarray(J, obj);
|
||||
obj->extensible = 0;
|
||||
|
||||
if (obj->properties->level)
|
||||
O_seal_walk(J, obj->properties);
|
||||
|
||||
js_copy(J, 1);
|
||||
}
|
||||
|
||||
static int O_isSealed_walk(js_State *J, js_Property *ref)
|
||||
{
|
||||
if (ref->left->level)
|
||||
if (!O_isSealed_walk(J, ref->left))
|
||||
return 0;
|
||||
if (!(ref->atts & JS_DONTCONF))
|
||||
return 0;
|
||||
if (ref->right->level)
|
||||
if (!O_isSealed_walk(J, ref->right))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void O_isSealed(js_State *J)
|
||||
{
|
||||
js_Object *obj;
|
||||
|
||||
if (!js_isobject(J, 1))
|
||||
js_typeerror(J, "not an object");
|
||||
|
||||
obj = js_toobject(J, 1);
|
||||
if (obj->extensible) {
|
||||
js_pushboolean(J, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj->properties->level)
|
||||
js_pushboolean(J, O_isSealed_walk(J, obj->properties));
|
||||
else
|
||||
js_pushboolean(J, 1);
|
||||
}
|
||||
|
||||
static void O_freeze_walk(js_State *J, js_Property *ref)
|
||||
{
|
||||
if (ref->left->level)
|
||||
O_freeze_walk(J, ref->left);
|
||||
ref->atts |= JS_READONLY | JS_DONTCONF;
|
||||
if (ref->right->level)
|
||||
O_freeze_walk(J, ref->right);
|
||||
}
|
||||
|
||||
static void O_freeze(js_State *J)
|
||||
{
|
||||
js_Object *obj;
|
||||
|
||||
if (!js_isobject(J, 1))
|
||||
js_typeerror(J, "not an object");
|
||||
|
||||
obj = js_toobject(J, 1);
|
||||
jsR_unflattenarray(J, obj);
|
||||
obj->extensible = 0;
|
||||
|
||||
if (obj->properties->level)
|
||||
O_freeze_walk(J, obj->properties);
|
||||
|
||||
js_copy(J, 1);
|
||||
}
|
||||
|
||||
static int O_isFrozen_walk(js_State *J, js_Property *ref)
|
||||
{
|
||||
if (ref->left->level)
|
||||
if (!O_isFrozen_walk(J, ref->left))
|
||||
return 0;
|
||||
if (!(ref->atts & JS_READONLY))
|
||||
return 0;
|
||||
if (!(ref->atts & JS_DONTCONF))
|
||||
return 0;
|
||||
if (ref->right->level)
|
||||
if (!O_isFrozen_walk(J, ref->right))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void O_isFrozen(js_State *J)
|
||||
{
|
||||
js_Object *obj;
|
||||
|
||||
if (!js_isobject(J, 1))
|
||||
js_typeerror(J, "not an object");
|
||||
|
||||
obj = js_toobject(J, 1);
|
||||
|
||||
if (obj->properties->level) {
|
||||
if (!O_isFrozen_walk(J, obj->properties)) {
|
||||
js_pushboolean(J, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
js_pushboolean(J, !obj->extensible);
|
||||
}
|
||||
|
||||
void jsB_initobject(js_State *J)
|
||||
{
|
||||
js_pushobject(J, J->Object_prototype);
|
||||
{
|
||||
jsB_propf(J, "Object.prototype.toString", Op_toString, 0);
|
||||
jsB_propf(J, "Object.prototype.toLocaleString", Op_toString, 0);
|
||||
jsB_propf(J, "Object.prototype.valueOf", Op_valueOf, 0);
|
||||
jsB_propf(J, "Object.prototype.hasOwnProperty", Op_hasOwnProperty, 1);
|
||||
jsB_propf(J, "Object.prototype.isPrototypeOf", Op_isPrototypeOf, 1);
|
||||
jsB_propf(J, "Object.prototype.propertyIsEnumerable", Op_propertyIsEnumerable, 1);
|
||||
}
|
||||
js_newcconstructor(J, jsB_Object, jsB_new_Object, "Object", 1);
|
||||
{
|
||||
/* ES5 */
|
||||
jsB_propf(J, "Object.getPrototypeOf", O_getPrototypeOf, 1);
|
||||
jsB_propf(J, "Object.getOwnPropertyDescriptor", O_getOwnPropertyDescriptor, 2);
|
||||
jsB_propf(J, "Object.getOwnPropertyNames", O_getOwnPropertyNames, 1);
|
||||
jsB_propf(J, "Object.create", O_create, 2);
|
||||
jsB_propf(J, "Object.defineProperty", O_defineProperty, 3);
|
||||
jsB_propf(J, "Object.defineProperties", O_defineProperties, 2);
|
||||
jsB_propf(J, "Object.seal", O_seal, 1);
|
||||
jsB_propf(J, "Object.freeze", O_freeze, 1);
|
||||
jsB_propf(J, "Object.preventExtensions", O_preventExtensions, 1);
|
||||
jsB_propf(J, "Object.isSealed", O_isSealed, 1);
|
||||
jsB_propf(J, "Object.isFrozen", O_isFrozen, 1);
|
||||
jsB_propf(J, "Object.isExtensible", O_isExtensible, 1);
|
||||
jsB_propf(J, "Object.keys", O_keys, 1);
|
||||
}
|
||||
js_defglobal(J, "Object", JS_DONTENUM);
|
||||
}
|
422
src/mujs/json.c
Normal file
422
src/mujs/json.c
Normal file
|
@ -0,0 +1,422 @@
|
|||
#include "jsi.h"
|
||||
#include "utf.h"
|
||||
|
||||
int js_isnumberobject(js_State *J, int idx)
|
||||
{
|
||||
return js_isobject(J, idx) && js_toobject(J, idx)->type == JS_CNUMBER;
|
||||
}
|
||||
|
||||
int js_isstringobject(js_State *J, int idx)
|
||||
{
|
||||
return js_isobject(J, idx) && js_toobject(J, idx)->type == JS_CSTRING;
|
||||
}
|
||||
|
||||
int js_isbooleanobject(js_State *J, int idx)
|
||||
{
|
||||
return js_isobject(J, idx) && js_toobject(J, idx)->type == JS_CBOOLEAN;
|
||||
}
|
||||
|
||||
int js_isdateobject(js_State *J, int idx)
|
||||
{
|
||||
return js_isobject(J, idx) && js_toobject(J, idx)->type == JS_CDATE;
|
||||
}
|
||||
|
||||
static void jsonnext(js_State *J)
|
||||
{
|
||||
J->lookahead = jsY_lexjson(J);
|
||||
}
|
||||
|
||||
static int jsonaccept(js_State *J, int t)
|
||||
{
|
||||
if (J->lookahead == t) {
|
||||
jsonnext(J);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void jsonexpect(js_State *J, int t)
|
||||
{
|
||||
if (!jsonaccept(J, t))
|
||||
js_syntaxerror(J, "JSON: unexpected token: %s (expected %s)",
|
||||
jsY_tokenstring(J->lookahead), jsY_tokenstring(t));
|
||||
}
|
||||
|
||||
static void jsonvalue(js_State *J)
|
||||
{
|
||||
int i;
|
||||
const char *name;
|
||||
|
||||
switch (J->lookahead) {
|
||||
case TK_STRING:
|
||||
js_pushstring(J, J->text);
|
||||
jsonnext(J);
|
||||
break;
|
||||
|
||||
case TK_NUMBER:
|
||||
js_pushnumber(J, J->number);
|
||||
jsonnext(J);
|
||||
break;
|
||||
|
||||
case '{':
|
||||
js_newobject(J);
|
||||
jsonnext(J);
|
||||
if (jsonaccept(J, '}'))
|
||||
return;
|
||||
do {
|
||||
if (J->lookahead != TK_STRING)
|
||||
js_syntaxerror(J, "JSON: unexpected token: %s (expected string)", jsY_tokenstring(J->lookahead));
|
||||
name = J->text;
|
||||
jsonnext(J);
|
||||
jsonexpect(J, ':');
|
||||
jsonvalue(J);
|
||||
js_setproperty(J, -2, name);
|
||||
} while (jsonaccept(J, ','));
|
||||
jsonexpect(J, '}');
|
||||
break;
|
||||
|
||||
case '[':
|
||||
js_newarray(J);
|
||||
jsonnext(J);
|
||||
i = 0;
|
||||
if (jsonaccept(J, ']'))
|
||||
return;
|
||||
do {
|
||||
jsonvalue(J);
|
||||
js_setindex(J, -2, i++);
|
||||
} while (jsonaccept(J, ','));
|
||||
jsonexpect(J, ']');
|
||||
break;
|
||||
|
||||
case TK_TRUE:
|
||||
js_pushboolean(J, 1);
|
||||
jsonnext(J);
|
||||
break;
|
||||
|
||||
case TK_FALSE:
|
||||
js_pushboolean(J, 0);
|
||||
jsonnext(J);
|
||||
break;
|
||||
|
||||
case TK_NULL:
|
||||
js_pushnull(J);
|
||||
jsonnext(J);
|
||||
break;
|
||||
|
||||
default:
|
||||
js_syntaxerror(J, "JSON: unexpected token: %s", jsY_tokenstring(J->lookahead));
|
||||
}
|
||||
}
|
||||
|
||||
static void jsonrevive(js_State *J, const char *name)
|
||||
{
|
||||
const char *key;
|
||||
char buf[32];
|
||||
|
||||
/* revive is in 2 */
|
||||
/* holder is in -1 */
|
||||
|
||||
js_getproperty(J, -1, name); /* get value from holder */
|
||||
|
||||
if (js_isobject(J, -1)) {
|
||||
if (js_isarray(J, -1)) {
|
||||
int i = 0;
|
||||
int n = js_getlength(J, -1);
|
||||
for (i = 0; i < n; ++i) {
|
||||
jsonrevive(J, js_itoa(buf, i));
|
||||
if (js_isundefined(J, -1)) {
|
||||
js_pop(J, 1);
|
||||
js_delproperty(J, -1, buf);
|
||||
} else {
|
||||
js_setproperty(J, -2, buf);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
js_pushiterator(J, -1, 1);
|
||||
while ((key = js_nextiterator(J, -1))) {
|
||||
js_rot2(J);
|
||||
jsonrevive(J, key);
|
||||
if (js_isundefined(J, -1)) {
|
||||
js_pop(J, 1);
|
||||
js_delproperty(J, -1, key);
|
||||
} else {
|
||||
js_setproperty(J, -2, key);
|
||||
}
|
||||
js_rot2(J);
|
||||
}
|
||||
js_pop(J, 1);
|
||||
}
|
||||
}
|
||||
|
||||
js_copy(J, 2); /* reviver function */
|
||||
js_copy(J, -3); /* holder as this */
|
||||
js_pushstring(J, name); /* name */
|
||||
js_copy(J, -4); /* value */
|
||||
js_call(J, 2);
|
||||
js_rot2pop1(J); /* pop old value, leave new value on stack */
|
||||
}
|
||||
|
||||
static void JSON_parse(js_State *J)
|
||||
{
|
||||
const char *source = js_tostring(J, 1);
|
||||
jsY_initlex(J, "JSON", source);
|
||||
jsonnext(J);
|
||||
|
||||
if (js_iscallable(J, 2)) {
|
||||
js_newobject(J);
|
||||
jsonvalue(J);
|
||||
js_defproperty(J, -2, "", 0);
|
||||
jsonrevive(J, "");
|
||||
} else {
|
||||
jsonvalue(J);
|
||||
}
|
||||
}
|
||||
|
||||
static void fmtnum(js_State *J, js_Buffer **sb, double n)
|
||||
{
|
||||
if (isnan(n)) js_puts(J, sb, "null");
|
||||
else if (isinf(n)) js_puts(J, sb, "null");
|
||||
else if (n == 0) js_puts(J, sb, "0");
|
||||
else {
|
||||
char buf[40];
|
||||
js_puts(J, sb, jsV_numbertostring(J, buf, n));
|
||||
}
|
||||
}
|
||||
|
||||
static void fmtstr(js_State *J, js_Buffer **sb, const char *s)
|
||||
{
|
||||
static const char *HEX = "0123456789abcdef";
|
||||
int i, n;
|
||||
Rune c;
|
||||
js_putc(J, sb, '"');
|
||||
while (*s) {
|
||||
n = chartorune(&c, s);
|
||||
switch (c) {
|
||||
case '"': js_puts(J, sb, "\\\""); break;
|
||||
case '\\': js_puts(J, sb, "\\\\"); break;
|
||||
case '\b': js_puts(J, sb, "\\b"); break;
|
||||
case '\f': js_puts(J, sb, "\\f"); break;
|
||||
case '\n': js_puts(J, sb, "\\n"); break;
|
||||
case '\r': js_puts(J, sb, "\\r"); break;
|
||||
case '\t': js_puts(J, sb, "\\t"); break;
|
||||
default:
|
||||
if (c < ' ' || (c >= 0xd800 && c <= 0xdfff)) {
|
||||
js_putc(J, sb, '\\');
|
||||
js_putc(J, sb, 'u');
|
||||
js_putc(J, sb, HEX[(c>>12)&15]);
|
||||
js_putc(J, sb, HEX[(c>>8)&15]);
|
||||
js_putc(J, sb, HEX[(c>>4)&15]);
|
||||
js_putc(J, sb, HEX[c&15]);
|
||||
} else if (c < 128) {
|
||||
js_putc(J, sb, c);
|
||||
} else {
|
||||
for (i = 0; i < n; ++i)
|
||||
js_putc(J, sb, s[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
s += n;
|
||||
}
|
||||
js_putc(J, sb, '"');
|
||||
}
|
||||
|
||||
static void fmtindent(js_State *J, js_Buffer **sb, const char *gap, int level)
|
||||
{
|
||||
js_putc(J, sb, '\n');
|
||||
while (level--)
|
||||
js_puts(J, sb, gap);
|
||||
}
|
||||
|
||||
static int fmtvalue(js_State *J, js_Buffer **sb, const char *key, const char *gap, int level);
|
||||
|
||||
static int filterprop(js_State *J, const char *key)
|
||||
{
|
||||
int i, n, found;
|
||||
/* replacer/property-list is in stack slot 2 */
|
||||
if (js_isarray(J, 2)) {
|
||||
found = 0;
|
||||
n = js_getlength(J, 2);
|
||||
for (i = 0; i < n && !found; ++i) {
|
||||
js_getindex(J, 2, i);
|
||||
if (js_isstring(J, -1) || js_isnumber(J, -1) ||
|
||||
js_isstringobject(J, -1) || js_isnumberobject(J, -1))
|
||||
found = !strcmp(key, js_tostring(J, -1));
|
||||
js_pop(J, 1);
|
||||
}
|
||||
return found;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void fmtobject(js_State *J, js_Buffer **sb, js_Object *obj, const char *gap, int level)
|
||||
{
|
||||
const char *key;
|
||||
int save;
|
||||
int i, n;
|
||||
|
||||
n = js_gettop(J) - 1;
|
||||
for (i = 4; i < n; ++i)
|
||||
if (js_isobject(J, i))
|
||||
if (js_toobject(J, i) == js_toobject(J, -1))
|
||||
js_typeerror(J, "cyclic object value");
|
||||
|
||||
n = 0;
|
||||
js_putc(J, sb, '{');
|
||||
js_pushiterator(J, -1, 1);
|
||||
while ((key = js_nextiterator(J, -1))) {
|
||||
if (filterprop(J, key)) {
|
||||
save = (*sb)->n;
|
||||
if (n) js_putc(J, sb, ',');
|
||||
if (gap) fmtindent(J, sb, gap, level + 1);
|
||||
fmtstr(J, sb, key);
|
||||
js_putc(J, sb, ':');
|
||||
if (gap)
|
||||
js_putc(J, sb, ' ');
|
||||
js_rot2(J);
|
||||
if (!fmtvalue(J, sb, key, gap, level + 1))
|
||||
(*sb)->n = save;
|
||||
else
|
||||
++n;
|
||||
js_rot2(J);
|
||||
}
|
||||
}
|
||||
js_pop(J, 1);
|
||||
if (gap && n) fmtindent(J, sb, gap, level);
|
||||
js_putc(J, sb, '}');
|
||||
}
|
||||
|
||||
static void fmtarray(js_State *J, js_Buffer **sb, const char *gap, int level)
|
||||
{
|
||||
int n, i;
|
||||
char buf[32];
|
||||
|
||||
n = js_gettop(J) - 1;
|
||||
for (i = 4; i < n; ++i)
|
||||
if (js_isobject(J, i))
|
||||
if (js_toobject(J, i) == js_toobject(J, -1))
|
||||
js_typeerror(J, "cyclic object value");
|
||||
|
||||
js_putc(J, sb, '[');
|
||||
n = js_getlength(J, -1);
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (i) js_putc(J, sb, ',');
|
||||
if (gap) fmtindent(J, sb, gap, level + 1);
|
||||
if (!fmtvalue(J, sb, js_itoa(buf, i), gap, level + 1))
|
||||
js_puts(J, sb, "null");
|
||||
}
|
||||
if (gap && n) fmtindent(J, sb, gap, level);
|
||||
js_putc(J, sb, ']');
|
||||
}
|
||||
|
||||
static int fmtvalue(js_State *J, js_Buffer **sb, const char *key, const char *gap, int level)
|
||||
{
|
||||
/* replacer/property-list is in 2 */
|
||||
/* holder is in -1 */
|
||||
|
||||
js_getproperty(J, -1, key);
|
||||
|
||||
if (js_isobject(J, -1)) {
|
||||
if (js_hasproperty(J, -1, "toJSON")) {
|
||||
if (js_iscallable(J, -1)) {
|
||||
js_copy(J, -2);
|
||||
js_pushstring(J, key);
|
||||
js_call(J, 1);
|
||||
js_rot2pop1(J);
|
||||
} else {
|
||||
js_pop(J, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (js_iscallable(J, 2)) {
|
||||
js_copy(J, 2); /* replacer function */
|
||||
js_copy(J, -3); /* holder as this */
|
||||
js_pushstring(J, key); /* name */
|
||||
js_copy(J, -4); /* old value */
|
||||
js_call(J, 2);
|
||||
js_rot2pop1(J); /* pop old value, leave new value on stack */
|
||||
}
|
||||
|
||||
if (js_isobject(J, -1) && !js_iscallable(J, -1)) {
|
||||
js_Object *obj = js_toobject(J, -1);
|
||||
switch (obj->type) {
|
||||
case JS_CNUMBER: fmtnum(J, sb, obj->u.number); break;
|
||||
case JS_CSTRING: fmtstr(J, sb, obj->u.s.string); break;
|
||||
case JS_CBOOLEAN: js_puts(J, sb, obj->u.boolean ? "true" : "false"); break;
|
||||
case JS_CARRAY: fmtarray(J, sb, gap, level); break;
|
||||
default: fmtobject(J, sb, obj, gap, level); break;
|
||||
}
|
||||
}
|
||||
else if (js_isboolean(J, -1))
|
||||
js_puts(J, sb, js_toboolean(J, -1) ? "true" : "false");
|
||||
else if (js_isnumber(J, -1))
|
||||
fmtnum(J, sb, js_tonumber(J, -1));
|
||||
else if (js_isstring(J, -1))
|
||||
fmtstr(J, sb, js_tostring(J, -1));
|
||||
else if (js_isnull(J, -1))
|
||||
js_puts(J, sb, "null");
|
||||
else {
|
||||
js_pop(J, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
js_pop(J, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void JSON_stringify(js_State *J)
|
||||
{
|
||||
js_Buffer *sb = NULL;
|
||||
char buf[12];
|
||||
/* NOTE: volatile to silence GCC warning about longjmp clobbering a variable */
|
||||
const char * volatile gap;
|
||||
const char *s;
|
||||
int n;
|
||||
|
||||
gap = NULL;
|
||||
|
||||
if (js_isnumber(J, 3) || js_isnumberobject(J, 3)) {
|
||||
n = js_tointeger(J, 3);
|
||||
if (n < 0) n = 0;
|
||||
if (n > 10) n = 10;
|
||||
memset(buf, ' ', n);
|
||||
buf[n] = 0;
|
||||
if (n > 0) gap = buf;
|
||||
} else if (js_isstring(J, 3) || js_isstringobject(J, 3)) {
|
||||
s = js_tostring(J, 3);
|
||||
n = strlen(s);
|
||||
if (n > 10) n = 10;
|
||||
memcpy(buf, s, n);
|
||||
buf[n] = 0;
|
||||
if (n > 0) gap = buf;
|
||||
}
|
||||
|
||||
if (js_try(J)) {
|
||||
js_free(J, sb);
|
||||
js_throw(J);
|
||||
}
|
||||
|
||||
js_newobject(J); /* wrapper */
|
||||
js_copy(J, 1);
|
||||
js_defproperty(J, -2, "", 0);
|
||||
if (!fmtvalue(J, &sb, "", gap, 0)) {
|
||||
js_pushundefined(J);
|
||||
} else {
|
||||
js_putc(J, &sb, 0);
|
||||
js_pushstring(J, sb ? sb->s : "");
|
||||
js_rot2pop1(J);
|
||||
}
|
||||
|
||||
js_endtry(J);
|
||||
js_free(J, sb);
|
||||
}
|
||||
|
||||
void jsB_initjson(js_State *J)
|
||||
{
|
||||
js_pushobject(J, jsV_newobject(J, JS_CJSON, J->Object_prototype));
|
||||
{
|
||||
jsB_propf(J, "JSON.parse", JSON_parse, 2);
|
||||
jsB_propf(J, "JSON.stringify", JSON_stringify, 3);
|
||||
}
|
||||
js_defglobal(J, "JSON", JS_DONTENUM);
|
||||
}
|
1065
src/mujs/jsparse.c
Normal file
1065
src/mujs/jsparse.c
Normal file
File diff suppressed because it is too large
Load diff
341
src/mujs/jsproperty.c
Normal file
341
src/mujs/jsproperty.c
Normal file
|
@ -0,0 +1,341 @@
|
|||
#include "jsi.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
/*
|
||||
Use an AA-tree to quickly look up properties in objects:
|
||||
|
||||
The level of every leaf node is one.
|
||||
The level of every left child is one less than its parent.
|
||||
The level of every right child is equal or one less than its parent.
|
||||
The level of every right grandchild is less than its grandparent.
|
||||
Every node of level greater than one has two children.
|
||||
|
||||
A link where the child's level is equal to that of its parent is called a horizontal link.
|
||||
Individual right horizontal links are allowed, but consecutive ones are forbidden.
|
||||
Left horizontal links are forbidden.
|
||||
|
||||
skew() fixes left horizontal links.
|
||||
split() fixes consecutive right horizontal links.
|
||||
*/
|
||||
|
||||
static js_Property sentinel = {
|
||||
&sentinel, &sentinel,
|
||||
0, 0,
|
||||
{ { {0}, JS_TUNDEFINED } },
|
||||
NULL, NULL, ""
|
||||
};
|
||||
|
||||
static js_Property *newproperty(js_State *J, js_Object *obj, const char *name)
|
||||
{
|
||||
int n = strlen(name) + 1;
|
||||
js_Property *node = js_malloc(J, offsetof(js_Property, name) + n);
|
||||
node->left = node->right = &sentinel;
|
||||
node->level = 1;
|
||||
node->atts = 0;
|
||||
node->value.t.type = JS_TUNDEFINED;
|
||||
node->value.u.number = 0;
|
||||
node->getter = NULL;
|
||||
node->setter = NULL;
|
||||
memcpy(node->name, name, n);
|
||||
++obj->count;
|
||||
++J->gccounter;
|
||||
return node;
|
||||
}
|
||||
|
||||
static js_Property *lookup(js_Property *node, const char *name)
|
||||
{
|
||||
while (node != &sentinel) {
|
||||
int c = strcmp(name, node->name);
|
||||
if (c == 0)
|
||||
return node;
|
||||
else if (c < 0)
|
||||
node = node->left;
|
||||
else
|
||||
node = node->right;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static js_Property *skew(js_Property *node)
|
||||
{
|
||||
if (node->left->level == node->level) {
|
||||
js_Property *temp = node;
|
||||
node = node->left;
|
||||
temp->left = node->right;
|
||||
node->right = temp;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
static js_Property *split(js_Property *node)
|
||||
{
|
||||
if (node->right->right->level == node->level) {
|
||||
js_Property *temp = node;
|
||||
node = node->right;
|
||||
temp->right = node->left;
|
||||
node->left = temp;
|
||||
++node->level;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
static js_Property *insert(js_State *J, js_Object *obj, js_Property *node, const char *name, js_Property **result)
|
||||
{
|
||||
if (node != &sentinel) {
|
||||
int c = strcmp(name, node->name);
|
||||
if (c < 0)
|
||||
node->left = insert(J, obj, node->left, name, result);
|
||||
else if (c > 0)
|
||||
node->right = insert(J, obj, node->right, name, result);
|
||||
else
|
||||
return *result = node;
|
||||
node = skew(node);
|
||||
node = split(node);
|
||||
return node;
|
||||
}
|
||||
return *result = newproperty(J, obj, name);
|
||||
}
|
||||
|
||||
static void freeproperty(js_State *J, js_Object *obj, js_Property *node)
|
||||
{
|
||||
js_free(J, node);
|
||||
--obj->count;
|
||||
}
|
||||
|
||||
static js_Property *unlinkproperty(js_Property *node, const char *name, js_Property **garbage)
|
||||
{
|
||||
js_Property *temp, *a, *b;
|
||||
if (node != &sentinel) {
|
||||
int c = strcmp(name, node->name);
|
||||
if (c < 0) {
|
||||
node->left = unlinkproperty(node->left, name, garbage);
|
||||
} else if (c > 0) {
|
||||
node->right = unlinkproperty(node->right, name, garbage);
|
||||
} else {
|
||||
*garbage = node;
|
||||
if (node->left == &sentinel && node->right == &sentinel) {
|
||||
return &sentinel;
|
||||
}
|
||||
else if (node->left == &sentinel) {
|
||||
a = node->right;
|
||||
while (a->left != &sentinel)
|
||||
a = a->left;
|
||||
b = unlinkproperty(node->right, a->name, &temp);
|
||||
temp->level = node->level;
|
||||
temp->left = node->left;
|
||||
temp->right = b;
|
||||
node = temp;
|
||||
}
|
||||
else {
|
||||
a = node->left;
|
||||
while (a->right != &sentinel)
|
||||
a = a->right;
|
||||
b = unlinkproperty(node->left, a->name, &temp);
|
||||
temp->level = node->level;
|
||||
temp->left = b;
|
||||
temp->right = node->right;
|
||||
node = temp;
|
||||
}
|
||||
}
|
||||
|
||||
if (node->left->level < node->level - 1 || node->right->level < node->level - 1)
|
||||
{
|
||||
if (node->right->level > --node->level)
|
||||
node->right->level = node->level;
|
||||
node = skew(node);
|
||||
node->right = skew(node->right);
|
||||
node->right->right = skew(node->right->right);
|
||||
node = split(node);
|
||||
node->right = split(node->right);
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
static js_Property *deleteproperty(js_State *J, js_Object *obj, js_Property *tree, const char *name)
|
||||
{
|
||||
js_Property *garbage = &sentinel;
|
||||
tree = unlinkproperty(tree, name, &garbage);
|
||||
if (garbage != &sentinel)
|
||||
freeproperty(J, obj, garbage);
|
||||
return tree;
|
||||
}
|
||||
|
||||
js_Object *jsV_newobject(js_State *J, enum js_Class type, js_Object *prototype)
|
||||
{
|
||||
js_Object *obj = js_malloc(J, sizeof *obj);
|
||||
memset(obj, 0, sizeof *obj);
|
||||
obj->gcmark = 0;
|
||||
obj->gcnext = J->gcobj;
|
||||
J->gcobj = obj;
|
||||
++J->gccounter;
|
||||
|
||||
obj->type = type;
|
||||
obj->properties = &sentinel;
|
||||
obj->prototype = prototype;
|
||||
obj->extensible = 1;
|
||||
return obj;
|
||||
}
|
||||
|
||||
js_Property *jsV_getownproperty(js_State *J, js_Object *obj, const char *name)
|
||||
{
|
||||
return lookup(obj->properties, name);
|
||||
}
|
||||
|
||||
js_Property *jsV_getpropertyx(js_State *J, js_Object *obj, const char *name, int *own)
|
||||
{
|
||||
*own = 1;
|
||||
do {
|
||||
js_Property *ref = lookup(obj->properties, name);
|
||||
if (ref)
|
||||
return ref;
|
||||
obj = obj->prototype;
|
||||
*own = 0;
|
||||
} while (obj);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
js_Property *jsV_getproperty(js_State *J, js_Object *obj, const char *name)
|
||||
{
|
||||
do {
|
||||
js_Property *ref = lookup(obj->properties, name);
|
||||
if (ref)
|
||||
return ref;
|
||||
obj = obj->prototype;
|
||||
} while (obj);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static js_Property *jsV_getenumproperty(js_State *J, js_Object *obj, const char *name)
|
||||
{
|
||||
do {
|
||||
js_Property *ref = lookup(obj->properties, name);
|
||||
if (ref && !(ref->atts & JS_DONTENUM))
|
||||
return ref;
|
||||
obj = obj->prototype;
|
||||
} while (obj);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
js_Property *jsV_setproperty(js_State *J, js_Object *obj, const char *name)
|
||||
{
|
||||
js_Property *result;
|
||||
|
||||
if (!obj->extensible) {
|
||||
result = lookup(obj->properties, name);
|
||||
if (J->strict && !result)
|
||||
js_typeerror(J, "object is non-extensible");
|
||||
return result;
|
||||
}
|
||||
|
||||
obj->properties = insert(J, obj, obj->properties, name, &result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void jsV_delproperty(js_State *J, js_Object *obj, const char *name)
|
||||
{
|
||||
obj->properties = deleteproperty(J, obj, obj->properties, name);
|
||||
}
|
||||
|
||||
/* Flatten hierarchy of enumerable properties into an iterator object */
|
||||
|
||||
static js_Iterator *itnewnode(js_State *J, const char *name, js_Iterator *next) {
|
||||
int n = strlen(name) + 1;
|
||||
js_Iterator *node = js_malloc(J, offsetof(js_Iterator, name) + n);
|
||||
node->next = next;
|
||||
memcpy(node->name, name, n);
|
||||
return node;
|
||||
}
|
||||
|
||||
static js_Iterator *itwalk(js_State *J, js_Iterator *iter, js_Property *prop, js_Object *seen)
|
||||
{
|
||||
if (prop->right != &sentinel)
|
||||
iter = itwalk(J, iter, prop->right, seen);
|
||||
if (!(prop->atts & JS_DONTENUM)) {
|
||||
if (!seen || !jsV_getenumproperty(J, seen, prop->name)) {
|
||||
iter = itnewnode(J, prop->name, iter);
|
||||
}
|
||||
}
|
||||
if (prop->left != &sentinel)
|
||||
iter = itwalk(J, iter, prop->left, seen);
|
||||
return iter;
|
||||
}
|
||||
|
||||
static js_Iterator *itflatten(js_State *J, js_Object *obj)
|
||||
{
|
||||
js_Iterator *iter = NULL;
|
||||
if (obj->prototype)
|
||||
iter = itflatten(J, obj->prototype);
|
||||
if (obj->properties != &sentinel)
|
||||
iter = itwalk(J, iter, obj->properties, obj->prototype);
|
||||
return iter;
|
||||
}
|
||||
|
||||
js_Object *jsV_newiterator(js_State *J, js_Object *obj, int own)
|
||||
{
|
||||
js_Object *io = jsV_newobject(J, JS_CITERATOR, NULL);
|
||||
io->u.iter.target = obj;
|
||||
io->u.iter.i = 0;
|
||||
io->u.iter.n = 0;
|
||||
if (own) {
|
||||
io->u.iter.head = NULL;
|
||||
if (obj->properties != &sentinel)
|
||||
io->u.iter.head = itwalk(J, io->u.iter.head, obj->properties, NULL);
|
||||
} else {
|
||||
io->u.iter.head = itflatten(J, obj);
|
||||
}
|
||||
io->u.iter.current = io->u.iter.head;
|
||||
|
||||
if (obj->type == JS_CSTRING)
|
||||
io->u.iter.n = obj->u.s.length;
|
||||
|
||||
if (obj->type == JS_CARRAY && obj->u.a.simple)
|
||||
io->u.iter.n = obj->u.a.flat_length;
|
||||
|
||||
return io;
|
||||
}
|
||||
|
||||
const char *jsV_nextiterator(js_State *J, js_Object *io)
|
||||
{
|
||||
if (io->type != JS_CITERATOR)
|
||||
js_typeerror(J, "not an iterator");
|
||||
if (io->u.iter.i < io->u.iter.n) {
|
||||
js_itoa(J->scratch, io->u.iter.i);
|
||||
io->u.iter.i++;
|
||||
return J->scratch;
|
||||
}
|
||||
while (io->u.iter.current) {
|
||||
const char *name = io->u.iter.current->name;
|
||||
io->u.iter.current = io->u.iter.current->next;
|
||||
if (jsV_getproperty(J, io->u.iter.target, name))
|
||||
return name;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Walk all the properties and delete them one by one for arrays */
|
||||
|
||||
void jsV_resizearray(js_State *J, js_Object *obj, int newlen)
|
||||
{
|
||||
char buf[32];
|
||||
const char *s;
|
||||
int k;
|
||||
assert(!obj->u.a.simple);
|
||||
if (newlen < obj->u.a.length) {
|
||||
if (obj->u.a.length > obj->count * 2) {
|
||||
js_Object *it = jsV_newiterator(J, obj, 1);
|
||||
while ((s = jsV_nextiterator(J, it))) {
|
||||
k = jsV_numbertointeger(jsV_stringtonumber(J, s));
|
||||
if (k >= newlen && !strcmp(s, jsV_numbertostring(J, buf, k)))
|
||||
jsV_delproperty(J, obj, s);
|
||||
}
|
||||
} else {
|
||||
for (k = newlen; k < obj->u.a.length; ++k) {
|
||||
jsV_delproperty(J, obj, js_itoa(buf, k));
|
||||
}
|
||||
}
|
||||
}
|
||||
obj->u.a.length = newlen;
|
||||
}
|
232
src/mujs/jsregexp.c
Normal file
232
src/mujs/jsregexp.c
Normal file
|
@ -0,0 +1,232 @@
|
|||
#include "jsi.h"
|
||||
#include "regexp.h"
|
||||
|
||||
static char *escaperegexp(js_State *J, const char *pattern) {
|
||||
char *copy, *p;
|
||||
const char *s;
|
||||
int n = 0;
|
||||
for (s = pattern; *s; ++s) {
|
||||
if (*s == '/')
|
||||
++n;
|
||||
++n;
|
||||
}
|
||||
copy = p = js_malloc(J, n+1);
|
||||
for (s = pattern; *s; ++s) {
|
||||
if (*s == '/')
|
||||
*p++ = '\\';
|
||||
*p++ = *s;
|
||||
}
|
||||
*p = 0;
|
||||
return copy;
|
||||
}
|
||||
|
||||
static void js_newregexpx(js_State *J, const char *pattern, int flags, int is_clone)
|
||||
{
|
||||
const char *error;
|
||||
js_Object *obj;
|
||||
Reprog *prog;
|
||||
int opts;
|
||||
|
||||
obj = jsV_newobject(J, JS_CREGEXP, J->RegExp_prototype);
|
||||
|
||||
opts = 0;
|
||||
if (flags & JS_REGEXP_I) opts |= REG_ICASE;
|
||||
if (flags & JS_REGEXP_M) opts |= REG_NEWLINE;
|
||||
|
||||
prog = js_regcompx(J->alloc, J->actx, pattern, opts, &error);
|
||||
if (!prog)
|
||||
js_syntaxerror(J, "regular expression: %s", error);
|
||||
|
||||
obj->u.r.prog = prog;
|
||||
obj->u.r.source = is_clone ? js_strdup(J, pattern) : escaperegexp(J, pattern);
|
||||
obj->u.r.flags = flags;
|
||||
obj->u.r.last = 0;
|
||||
js_pushobject(J, obj);
|
||||
}
|
||||
|
||||
void js_newregexp(js_State *J, const char *pattern, int flags)
|
||||
{
|
||||
js_newregexpx(J, pattern, flags, 0);
|
||||
}
|
||||
|
||||
void js_RegExp_prototype_exec(js_State *J, js_Regexp *re, const char *text)
|
||||
{
|
||||
const char *haystack;
|
||||
int result;
|
||||
int i;
|
||||
int opts;
|
||||
Resub m;
|
||||
|
||||
haystack = text;
|
||||
opts = 0;
|
||||
if (re->flags & JS_REGEXP_G) {
|
||||
if (re->last > strlen(haystack)) {
|
||||
re->last = 0;
|
||||
js_pushnull(J);
|
||||
return;
|
||||
}
|
||||
if (re->last > 0) {
|
||||
haystack = text + re->last;
|
||||
opts |= REG_NOTBOL;
|
||||
}
|
||||
}
|
||||
|
||||
result = js_regexec(re->prog, haystack, &m, opts);
|
||||
if (result < 0)
|
||||
js_error(J, "regexec failed");
|
||||
if (result == 0) {
|
||||
js_newarray(J);
|
||||
js_pushstring(J, text);
|
||||
js_setproperty(J, -2, "input");
|
||||
js_pushnumber(J, js_utfptrtoidx(text, m.sub[0].sp));
|
||||
js_setproperty(J, -2, "index");
|
||||
for (i = 0; i < m.nsub; ++i) {
|
||||
js_pushlstring(J, m.sub[i].sp, m.sub[i].ep - m.sub[i].sp);
|
||||
js_setindex(J, -2, i);
|
||||
}
|
||||
if (re->flags & JS_REGEXP_G)
|
||||
re->last = m.sub[0].ep - text;
|
||||
return;
|
||||
}
|
||||
|
||||
if (re->flags & JS_REGEXP_G)
|
||||
re->last = 0;
|
||||
|
||||
js_pushnull(J);
|
||||
}
|
||||
|
||||
static void Rp_test(js_State *J)
|
||||
{
|
||||
js_Regexp *re;
|
||||
const char *text;
|
||||
int result;
|
||||
int opts;
|
||||
Resub m;
|
||||
|
||||
re = js_toregexp(J, 0);
|
||||
text = js_tostring(J, 1);
|
||||
|
||||
opts = 0;
|
||||
if (re->flags & JS_REGEXP_G) {
|
||||
if (re->last > strlen(text)) {
|
||||
re->last = 0;
|
||||
js_pushboolean(J, 0);
|
||||
return;
|
||||
}
|
||||
if (re->last > 0) {
|
||||
text += re->last;
|
||||
opts |= REG_NOTBOL;
|
||||
}
|
||||
}
|
||||
|
||||
result = js_regexec(re->prog, text, &m, opts);
|
||||
if (result < 0)
|
||||
js_error(J, "regexec failed");
|
||||
if (result == 0) {
|
||||
if (re->flags & JS_REGEXP_G)
|
||||
re->last = re->last + (m.sub[0].ep - text);
|
||||
js_pushboolean(J, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (re->flags & JS_REGEXP_G)
|
||||
re->last = 0;
|
||||
|
||||
js_pushboolean(J, 0);
|
||||
}
|
||||
|
||||
static void jsB_new_RegExp(js_State *J)
|
||||
{
|
||||
js_Regexp *old;
|
||||
const char *pattern;
|
||||
int flags;
|
||||
int is_clone = 0;
|
||||
|
||||
if (js_isregexp(J, 1)) {
|
||||
if (js_isdefined(J, 2))
|
||||
js_typeerror(J, "cannot supply flags when creating one RegExp from another");
|
||||
old = js_toregexp(J, 1);
|
||||
pattern = old->source;
|
||||
flags = old->flags;
|
||||
is_clone = 1;
|
||||
} else if (js_isundefined(J, 1)) {
|
||||
pattern = "(?:)";
|
||||
flags = 0;
|
||||
} else {
|
||||
pattern = js_tostring(J, 1);
|
||||
flags = 0;
|
||||
}
|
||||
|
||||
if (strlen(pattern) == 0)
|
||||
pattern = "(?:)";
|
||||
|
||||
if (js_isdefined(J, 2)) {
|
||||
const char *s = js_tostring(J, 2);
|
||||
int g = 0, i = 0, m = 0;
|
||||
while (*s) {
|
||||
if (*s == 'g') ++g;
|
||||
else if (*s == 'i') ++i;
|
||||
else if (*s == 'm') ++m;
|
||||
else js_syntaxerror(J, "invalid regular expression flag: '%c'", *s);
|
||||
++s;
|
||||
}
|
||||
if (g > 1) js_syntaxerror(J, "invalid regular expression flag: 'g'");
|
||||
if (i > 1) js_syntaxerror(J, "invalid regular expression flag: 'i'");
|
||||
if (m > 1) js_syntaxerror(J, "invalid regular expression flag: 'm'");
|
||||
if (g) flags |= JS_REGEXP_G;
|
||||
if (i) flags |= JS_REGEXP_I;
|
||||
if (m) flags |= JS_REGEXP_M;
|
||||
}
|
||||
|
||||
js_newregexpx(J, pattern, flags, is_clone);
|
||||
}
|
||||
|
||||
static void jsB_RegExp(js_State *J)
|
||||
{
|
||||
if (js_isregexp(J, 1))
|
||||
return;
|
||||
jsB_new_RegExp(J);
|
||||
}
|
||||
|
||||
static void Rp_toString(js_State *J)
|
||||
{
|
||||
js_Regexp *re;
|
||||
char * volatile out = NULL;
|
||||
|
||||
re = js_toregexp(J, 0);
|
||||
|
||||
if (js_try(J)) {
|
||||
js_free(J, out);
|
||||
js_throw(J);
|
||||
}
|
||||
|
||||
out = js_malloc(J, strlen(re->source) + 6); /* extra space for //gim */
|
||||
strcpy(out, "/");
|
||||
strcat(out, re->source);
|
||||
strcat(out, "/");
|
||||
if (re->flags & JS_REGEXP_G) strcat(out, "g");
|
||||
if (re->flags & JS_REGEXP_I) strcat(out, "i");
|
||||
if (re->flags & JS_REGEXP_M) strcat(out, "m");
|
||||
|
||||
js_pop(J, 0);
|
||||
js_pushstring(J, out);
|
||||
js_endtry(J);
|
||||
js_free(J, out);
|
||||
}
|
||||
|
||||
static void Rp_exec(js_State *J)
|
||||
{
|
||||
js_RegExp_prototype_exec(J, js_toregexp(J, 0), js_tostring(J, 1));
|
||||
}
|
||||
|
||||
void jsB_initregexp(js_State *J)
|
||||
{
|
||||
js_pushobject(J, J->RegExp_prototype);
|
||||
{
|
||||
jsB_propf(J, "RegExp.prototype.toString", Rp_toString, 0);
|
||||
jsB_propf(J, "RegExp.prototype.test", Rp_test, 0);
|
||||
jsB_propf(J, "RegExp.prototype.exec", Rp_exec, 0);
|
||||
}
|
||||
js_newcconstructor(J, jsB_RegExp, jsB_new_RegExp, "RegExp", 1);
|
||||
js_defglobal(J, "RegExp", JS_DONTENUM);
|
||||
}
|
285
src/mujs/jsrepr.c
Normal file
285
src/mujs/jsrepr.c
Normal file
|
@ -0,0 +1,285 @@
|
|||
#include "jsi.h"
|
||||
#include "utf.h"
|
||||
|
||||
static void reprvalue(js_State *J, js_Buffer **sb);
|
||||
|
||||
static void reprnum(js_State *J, js_Buffer **sb, double n)
|
||||
{
|
||||
char buf[40];
|
||||
if (n == 0 && signbit(n))
|
||||
js_puts(J, sb, "-0");
|
||||
else
|
||||
js_puts(J, sb, jsV_numbertostring(J, buf, n));
|
||||
}
|
||||
|
||||
static void reprstr(js_State *J, js_Buffer **sb, const char *s)
|
||||
{
|
||||
static const char *HEX = "0123456789ABCDEF";
|
||||
int i, n;
|
||||
Rune c;
|
||||
js_putc(J, sb, '"');
|
||||
while (*s) {
|
||||
n = chartorune(&c, s);
|
||||
switch (c) {
|
||||
case '"': js_puts(J, sb, "\\\""); break;
|
||||
case '\\': js_puts(J, sb, "\\\\"); break;
|
||||
case '\b': js_puts(J, sb, "\\b"); break;
|
||||
case '\f': js_puts(J, sb, "\\f"); break;
|
||||
case '\n': js_puts(J, sb, "\\n"); break;
|
||||
case '\r': js_puts(J, sb, "\\r"); break;
|
||||
case '\t': js_puts(J, sb, "\\t"); break;
|
||||
default:
|
||||
if (c < ' ') {
|
||||
js_putc(J, sb, '\\');
|
||||
js_putc(J, sb, 'x');
|
||||
js_putc(J, sb, HEX[(c>>4)&15]);
|
||||
js_putc(J, sb, HEX[c&15]);
|
||||
} else if (c < 128) {
|
||||
js_putc(J, sb, c);
|
||||
} else if (c < 0x10000) {
|
||||
js_putc(J, sb, '\\');
|
||||
js_putc(J, sb, 'u');
|
||||
js_putc(J, sb, HEX[(c>>12)&15]);
|
||||
js_putc(J, sb, HEX[(c>>8)&15]);
|
||||
js_putc(J, sb, HEX[(c>>4)&15]);
|
||||
js_putc(J, sb, HEX[c&15]);
|
||||
} else {
|
||||
for (i = 0; i < n; ++i)
|
||||
js_putc(J, sb, s[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
s += n;
|
||||
}
|
||||
js_putc(J, sb, '"');
|
||||
}
|
||||
|
||||
#ifndef isalpha
|
||||
#define isalpha(c) ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
|
||||
#endif
|
||||
#ifndef isdigit
|
||||
#define isdigit(c) (c >= '0' && c <= '9')
|
||||
#endif
|
||||
|
||||
static void reprident(js_State *J, js_Buffer **sb, const char *name)
|
||||
{
|
||||
const char *p = name;
|
||||
if (isdigit(*p))
|
||||
while (isdigit(*p))
|
||||
++p;
|
||||
else if (isalpha(*p) || *p == '_')
|
||||
while (isdigit(*p) || isalpha(*p) || *p == '_')
|
||||
++p;
|
||||
if (p > name && *p == 0)
|
||||
js_puts(J, sb, name);
|
||||
else
|
||||
reprstr(J, sb, name);
|
||||
}
|
||||
|
||||
static void reprobject(js_State *J, js_Buffer **sb)
|
||||
{
|
||||
const char *key;
|
||||
int i, n;
|
||||
|
||||
n = js_gettop(J) - 1;
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (js_isobject(J, i)) {
|
||||
if (js_toobject(J, i) == js_toobject(J, -1)) {
|
||||
js_puts(J, sb, "{}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
n = 0;
|
||||
js_putc(J, sb, '{');
|
||||
js_pushiterator(J, -1, 1);
|
||||
while ((key = js_nextiterator(J, -1))) {
|
||||
if (n++ > 0)
|
||||
js_puts(J, sb, ", ");
|
||||
reprident(J, sb, key);
|
||||
js_puts(J, sb, ": ");
|
||||
js_getproperty(J, -2, key);
|
||||
reprvalue(J, sb);
|
||||
js_pop(J, 1);
|
||||
}
|
||||
js_pop(J, 1);
|
||||
js_putc(J, sb, '}');
|
||||
}
|
||||
|
||||
static void reprarray(js_State *J, js_Buffer **sb)
|
||||
{
|
||||
int n, i;
|
||||
|
||||
n = js_gettop(J) - 1;
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (js_isobject(J, i)) {
|
||||
if (js_toobject(J, i) == js_toobject(J, -1)) {
|
||||
js_puts(J, sb, "[]");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
js_putc(J, sb, '[');
|
||||
n = js_getlength(J, -1);
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (i > 0)
|
||||
js_puts(J, sb, ", ");
|
||||
if (js_hasindex(J, -1, i)) {
|
||||
reprvalue(J, sb);
|
||||
js_pop(J, 1);
|
||||
}
|
||||
}
|
||||
js_putc(J, sb, ']');
|
||||
}
|
||||
|
||||
static void reprfun(js_State *J, js_Buffer **sb, js_Function *fun)
|
||||
{
|
||||
int i;
|
||||
js_puts(J, sb, "function ");
|
||||
js_puts(J, sb, fun->name);
|
||||
js_putc(J, sb, '(');
|
||||
for (i = 0; i < fun->numparams; ++i) {
|
||||
if (i > 0)
|
||||
js_puts(J, sb, ", ");
|
||||
js_puts(J, sb, fun->vartab[i]);
|
||||
}
|
||||
js_puts(J, sb, ") { [byte code] }");
|
||||
}
|
||||
|
||||
static void reprvalue(js_State *J, js_Buffer **sb)
|
||||
{
|
||||
if (js_isundefined(J, -1))
|
||||
js_puts(J, sb, "undefined");
|
||||
else if (js_isnull(J, -1))
|
||||
js_puts(J, sb, "null");
|
||||
else if (js_isboolean(J, -1))
|
||||
js_puts(J, sb, js_toboolean(J, -1) ? "true" : "false");
|
||||
else if (js_isnumber(J, -1))
|
||||
reprnum(J, sb, js_tonumber(J, -1));
|
||||
else if (js_isstring(J, -1))
|
||||
reprstr(J, sb, js_tostring(J, -1));
|
||||
else if (js_isobject(J, -1)) {
|
||||
js_Object *obj = js_toobject(J, -1);
|
||||
switch (obj->type) {
|
||||
default:
|
||||
reprobject(J, sb);
|
||||
break;
|
||||
case JS_CARRAY:
|
||||
reprarray(J, sb);
|
||||
break;
|
||||
case JS_CFUNCTION:
|
||||
case JS_CSCRIPT:
|
||||
reprfun(J, sb, obj->u.f.function);
|
||||
break;
|
||||
case JS_CCFUNCTION:
|
||||
js_puts(J, sb, "function ");
|
||||
js_puts(J, sb, obj->u.c.name);
|
||||
js_puts(J, sb, "() { [native code] }");
|
||||
break;
|
||||
case JS_CBOOLEAN:
|
||||
js_puts(J, sb, "(new Boolean(");
|
||||
js_puts(J, sb, obj->u.boolean ? "true" : "false");
|
||||
js_puts(J, sb, "))");
|
||||
break;
|
||||
case JS_CNUMBER:
|
||||
js_puts(J, sb, "(new Number(");
|
||||
reprnum(J, sb, obj->u.number);
|
||||
js_puts(J, sb, "))");
|
||||
break;
|
||||
case JS_CSTRING:
|
||||
js_puts(J, sb, "(new String(");
|
||||
reprstr(J, sb, obj->u.s.string);
|
||||
js_puts(J, sb, "))");
|
||||
break;
|
||||
case JS_CREGEXP:
|
||||
js_putc(J, sb, '/');
|
||||
js_puts(J, sb, obj->u.r.source);
|
||||
js_putc(J, sb, '/');
|
||||
if (obj->u.r.flags & JS_REGEXP_G) js_putc(J, sb, 'g');
|
||||
if (obj->u.r.flags & JS_REGEXP_I) js_putc(J, sb, 'i');
|
||||
if (obj->u.r.flags & JS_REGEXP_M) js_putc(J, sb, 'm');
|
||||
break;
|
||||
case JS_CDATE:
|
||||
{
|
||||
char buf[40];
|
||||
js_puts(J, sb, "(new Date(");
|
||||
js_puts(J, sb, jsV_numbertostring(J, buf, obj->u.number));
|
||||
js_puts(J, sb, "))");
|
||||
}
|
||||
break;
|
||||
case JS_CERROR:
|
||||
js_puts(J, sb, "(new ");
|
||||
js_getproperty(J, -1, "name");
|
||||
js_puts(J, sb, js_tostring(J, -1));
|
||||
js_pop(J, 1);
|
||||
js_putc(J, sb, '(');
|
||||
if (js_hasproperty(J, -1, "message")) {
|
||||
reprvalue(J, sb);
|
||||
js_pop(J, 1);
|
||||
}
|
||||
js_puts(J, sb, "))");
|
||||
break;
|
||||
case JS_CMATH:
|
||||
js_puts(J, sb, "Math");
|
||||
break;
|
||||
case JS_CJSON:
|
||||
js_puts(J, sb, "JSON");
|
||||
break;
|
||||
case JS_CITERATOR:
|
||||
js_puts(J, sb, "[iterator ");
|
||||
break;
|
||||
case JS_CUSERDATA:
|
||||
js_puts(J, sb, "[userdata ");
|
||||
js_puts(J, sb, obj->u.user.tag);
|
||||
js_putc(J, sb, ']');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void js_repr(js_State *J, int idx)
|
||||
{
|
||||
js_Buffer *sb = NULL;
|
||||
int savebot;
|
||||
|
||||
if (js_try(J)) {
|
||||
js_free(J, sb);
|
||||
js_throw(J);
|
||||
}
|
||||
|
||||
js_copy(J, idx);
|
||||
|
||||
savebot = J->bot;
|
||||
J->bot = J->top - 1;
|
||||
reprvalue(J, &sb);
|
||||
J->bot = savebot;
|
||||
|
||||
js_pop(J, 1);
|
||||
|
||||
js_putc(J, &sb, 0);
|
||||
js_pushstring(J, sb ? sb->s : "undefined");
|
||||
|
||||
js_endtry(J);
|
||||
js_free(J, sb);
|
||||
}
|
||||
|
||||
const char *js_torepr(js_State *J, int idx)
|
||||
{
|
||||
js_repr(J, idx);
|
||||
js_replace(J, idx < 0 ? idx-1 : idx);
|
||||
return js_tostring(J, idx);
|
||||
}
|
||||
|
||||
const char *js_tryrepr(js_State *J, int idx, const char *error)
|
||||
{
|
||||
const char *s;
|
||||
if (js_try(J)) {
|
||||
js_pop(J, 1);
|
||||
return error;
|
||||
}
|
||||
s = js_torepr(J, idx);
|
||||
js_endtry(J);
|
||||
return s;
|
||||
}
|
2063
src/mujs/jsrun.c
Normal file
2063
src/mujs/jsrun.c
Normal file
File diff suppressed because it is too large
Load diff
334
src/mujs/jsstate.c
Normal file
334
src/mujs/jsstate.c
Normal file
|
@ -0,0 +1,334 @@
|
|||
#include "jsi.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
static int js_ptry(js_State *J) {
|
||||
if (J->trytop == JS_TRYLIMIT) {
|
||||
J->stack[J->top].t.type = JS_TLITSTR;
|
||||
J->stack[J->top].u.litstr = "exception stack overflow";
|
||||
++J->top;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *js_defaultalloc(void *actx, void *ptr, int size)
|
||||
{
|
||||
if (size == 0) {
|
||||
free(ptr);
|
||||
return NULL;
|
||||
}
|
||||
return realloc(ptr, (size_t)size);
|
||||
}
|
||||
|
||||
static void js_defaultreport(js_State *J, const char *message)
|
||||
{
|
||||
fputs(message, stderr);
|
||||
fputc('\n', stderr);
|
||||
}
|
||||
|
||||
static void js_defaultpanic(js_State *J)
|
||||
{
|
||||
js_report(J, "uncaught exception");
|
||||
/* return to javascript to abort */
|
||||
}
|
||||
|
||||
int js_ploadstring(js_State *J, const char *filename, const char *source)
|
||||
{
|
||||
if (js_ptry(J))
|
||||
return 1;
|
||||
if (js_try(J))
|
||||
return 1;
|
||||
js_loadstring(J, filename, source);
|
||||
js_endtry(J);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int js_ploadfile(js_State *J, const char *filename)
|
||||
{
|
||||
if (js_ptry(J))
|
||||
return 1;
|
||||
if (js_try(J))
|
||||
return 1;
|
||||
js_loadfile(J, filename);
|
||||
js_endtry(J);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *js_trystring(js_State *J, int idx, const char *error)
|
||||
{
|
||||
const char *s;
|
||||
if (js_ptry(J)) {
|
||||
js_pop(J, 1);
|
||||
return error;
|
||||
}
|
||||
if (js_try(J)) {
|
||||
js_pop(J, 1);
|
||||
return error;
|
||||
}
|
||||
s = js_tostring(J, idx);
|
||||
js_endtry(J);
|
||||
return s;
|
||||
}
|
||||
|
||||
double js_trynumber(js_State *J, int idx, double error)
|
||||
{
|
||||
double v;
|
||||
if (js_ptry(J)) {
|
||||
js_pop(J, 1);
|
||||
return error;
|
||||
}
|
||||
if (js_try(J)) {
|
||||
js_pop(J, 1);
|
||||
return error;
|
||||
}
|
||||
v = js_tonumber(J, idx);
|
||||
js_endtry(J);
|
||||
return v;
|
||||
}
|
||||
|
||||
int js_tryinteger(js_State *J, int idx, int error)
|
||||
{
|
||||
int v;
|
||||
if (js_ptry(J)) {
|
||||
js_pop(J, 1);
|
||||
return error;
|
||||
}
|
||||
if (js_try(J)) {
|
||||
js_pop(J, 1);
|
||||
return error;
|
||||
}
|
||||
v = js_tointeger(J, idx);
|
||||
js_endtry(J);
|
||||
return v;
|
||||
}
|
||||
|
||||
int js_tryboolean(js_State *J, int idx, int error)
|
||||
{
|
||||
int v;
|
||||
if (js_ptry(J)) {
|
||||
js_pop(J, 1);
|
||||
return error;
|
||||
}
|
||||
if (js_try(J)) {
|
||||
js_pop(J, 1);
|
||||
return error;
|
||||
}
|
||||
v = js_toboolean(J, idx);
|
||||
js_endtry(J);
|
||||
return v;
|
||||
}
|
||||
|
||||
static void js_loadstringx(js_State *J, const char *filename, const char *source, int iseval)
|
||||
{
|
||||
js_Ast *P;
|
||||
js_Function *F;
|
||||
|
||||
if (js_try(J)) {
|
||||
jsP_freeparse(J);
|
||||
js_throw(J);
|
||||
}
|
||||
|
||||
P = jsP_parse(J, filename, source);
|
||||
F = jsC_compilescript(J, P, iseval ? J->strict : J->default_strict);
|
||||
jsP_freeparse(J);
|
||||
js_newscript(J, F, iseval ? (J->strict ? J->E : NULL) : J->GE);
|
||||
|
||||
js_endtry(J);
|
||||
}
|
||||
|
||||
void js_loadeval(js_State *J, const char *filename, const char *source)
|
||||
{
|
||||
js_loadstringx(J, filename, source, 1);
|
||||
}
|
||||
|
||||
void js_loadstring(js_State *J, const char *filename, const char *source)
|
||||
{
|
||||
js_loadstringx(J, filename, source, 0);
|
||||
}
|
||||
|
||||
void js_loadfile(js_State *J, const char *filename)
|
||||
{
|
||||
FILE *f;
|
||||
char *s, *p;
|
||||
int n, t;
|
||||
|
||||
f = fopen(filename, "rb");
|
||||
if (!f) {
|
||||
js_error(J, "cannot open file '%s': %s", filename, strerror(errno));
|
||||
}
|
||||
|
||||
if (fseek(f, 0, SEEK_END) < 0) {
|
||||
fclose(f);
|
||||
js_error(J, "cannot seek in file '%s': %s", filename, strerror(errno));
|
||||
}
|
||||
|
||||
n = ftell(f);
|
||||
if (n < 0) {
|
||||
fclose(f);
|
||||
js_error(J, "cannot tell in file '%s': %s", filename, strerror(errno));
|
||||
}
|
||||
|
||||
if (fseek(f, 0, SEEK_SET) < 0) {
|
||||
fclose(f);
|
||||
js_error(J, "cannot seek in file '%s': %s", filename, strerror(errno));
|
||||
}
|
||||
|
||||
if (js_try(J)) {
|
||||
fclose(f);
|
||||
js_throw(J);
|
||||
}
|
||||
s = js_malloc(J, n + 1); /* add space for string terminator */
|
||||
js_endtry(J);
|
||||
|
||||
t = fread(s, 1, (size_t)n, f);
|
||||
if (t != n) {
|
||||
js_free(J, s);
|
||||
fclose(f);
|
||||
js_error(J, "cannot read data from file '%s': %s", filename, strerror(errno));
|
||||
}
|
||||
|
||||
s[n] = 0; /* zero-terminate string containing file data */
|
||||
|
||||
if (js_try(J)) {
|
||||
js_free(J, s);
|
||||
fclose(f);
|
||||
js_throw(J);
|
||||
}
|
||||
|
||||
/* skip first line if it starts with "#!" */
|
||||
p = s;
|
||||
if (p[0] == '#' && p[1] == '!') {
|
||||
p += 2;
|
||||
while (*p && *p != '\n')
|
||||
++p;
|
||||
}
|
||||
|
||||
js_loadstring(J, filename, p);
|
||||
|
||||
js_free(J, s);
|
||||
fclose(f);
|
||||
js_endtry(J);
|
||||
}
|
||||
|
||||
int js_dostring(js_State *J, const char *source)
|
||||
{
|
||||
if (js_ptry(J)) {
|
||||
js_report(J, "exception stack overflow");
|
||||
js_pop(J, 1);
|
||||
return 1;
|
||||
}
|
||||
if (js_try(J)) {
|
||||
js_report(J, js_trystring(J, -1, "Error"));
|
||||
js_pop(J, 1);
|
||||
return 1;
|
||||
}
|
||||
js_loadstring(J, "[string]", source);
|
||||
js_pushundefined(J);
|
||||
js_call(J, 0);
|
||||
js_pop(J, 1);
|
||||
js_endtry(J);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int js_dofile(js_State *J, const char *filename)
|
||||
{
|
||||
if (js_ptry(J)) {
|
||||
js_report(J, "exception stack overflow");
|
||||
js_pop(J, 1);
|
||||
return 1;
|
||||
}
|
||||
if (js_try(J)) {
|
||||
js_report(J, js_trystring(J, -1, "Error"));
|
||||
js_pop(J, 1);
|
||||
return 1;
|
||||
}
|
||||
js_loadfile(J, filename);
|
||||
js_pushundefined(J);
|
||||
js_call(J, 0);
|
||||
js_pop(J, 1);
|
||||
js_endtry(J);
|
||||
return 0;
|
||||
}
|
||||
|
||||
js_Panic js_atpanic(js_State *J, js_Panic panic)
|
||||
{
|
||||
js_Panic old = J->panic;
|
||||
J->panic = panic;
|
||||
return old;
|
||||
}
|
||||
|
||||
void js_report(js_State *J, const char *message)
|
||||
{
|
||||
if (J->report)
|
||||
J->report(J, message);
|
||||
}
|
||||
|
||||
void js_setreport(js_State *J, js_Report report)
|
||||
{
|
||||
J->report = report;
|
||||
}
|
||||
|
||||
void js_setcontext(js_State *J, void *uctx)
|
||||
{
|
||||
J->uctx = uctx;
|
||||
}
|
||||
|
||||
void *js_getcontext(js_State *J)
|
||||
{
|
||||
return J->uctx;
|
||||
}
|
||||
|
||||
js_State *js_newstate(js_Alloc alloc, void *actx, int flags)
|
||||
{
|
||||
js_State *J;
|
||||
|
||||
assert(sizeof(js_Value) == 16);
|
||||
assert(soffsetof(js_Value, t.type) == 15);
|
||||
|
||||
if (!alloc)
|
||||
alloc = js_defaultalloc;
|
||||
|
||||
J = alloc(actx, NULL, sizeof *J);
|
||||
if (!J)
|
||||
return NULL;
|
||||
memset(J, 0, sizeof(*J));
|
||||
J->actx = actx;
|
||||
J->alloc = alloc;
|
||||
|
||||
if (flags & JS_STRICT)
|
||||
J->strict = J->default_strict = 1;
|
||||
|
||||
J->trace[0].name = "-top-";
|
||||
J->trace[0].file = "native";
|
||||
J->trace[0].line = 0;
|
||||
|
||||
J->report = js_defaultreport;
|
||||
J->panic = js_defaultpanic;
|
||||
|
||||
J->stack = alloc(actx, NULL, JS_STACKSIZE * sizeof *J->stack);
|
||||
if (!J->stack) {
|
||||
alloc(actx, J, 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
J->gcmark = 1;
|
||||
J->nextref = 0;
|
||||
J->gcthresh = 0; /* reaches stability within ~ 2-5 GC cycles */
|
||||
|
||||
if (js_try(J)) {
|
||||
js_freestate(J);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
J->R = jsV_newobject(J, JS_COBJECT, NULL);
|
||||
J->G = jsV_newobject(J, JS_COBJECT, NULL);
|
||||
J->E = jsR_newenvironment(J, J->G, NULL);
|
||||
J->GE = J->E;
|
||||
|
||||
jsB_init(J);
|
||||
|
||||
js_endtry(J);
|
||||
return J;
|
||||
}
|
848
src/mujs/jsstring.c
Normal file
848
src/mujs/jsstring.c
Normal file
|
@ -0,0 +1,848 @@
|
|||
#include "jsi.h"
|
||||
#include "utf.h"
|
||||
#include "regexp.h"
|
||||
|
||||
static int js_doregexec(js_State *J, Reprog *prog, const char *string, Resub *sub, int eflags)
|
||||
{
|
||||
int result = js_regexec(prog, string, sub, eflags);
|
||||
if (result < 0)
|
||||
js_error(J, "regexec failed");
|
||||
return result;
|
||||
}
|
||||
|
||||
static const char *checkstring(js_State *J, int idx)
|
||||
{
|
||||
if (!js_iscoercible(J, idx))
|
||||
js_typeerror(J, "string function called on null or undefined");
|
||||
return js_tostring(J, idx);
|
||||
}
|
||||
|
||||
int js_runeat(js_State *J, const char *s, int i)
|
||||
{
|
||||
Rune rune = EOF;
|
||||
while (i >= 0) {
|
||||
rune = *(unsigned char*)s;
|
||||
if (rune < Runeself) {
|
||||
if (rune == 0)
|
||||
return EOF;
|
||||
++s;
|
||||
--i;
|
||||
} else {
|
||||
s += chartorune(&rune, s);
|
||||
if (rune >= 0x10000)
|
||||
i -= 2;
|
||||
else
|
||||
--i;
|
||||
}
|
||||
}
|
||||
if (rune >= 0x10000) {
|
||||
/* high surrogate */
|
||||
if (i == -2)
|
||||
return 0xd800 + ((rune - 0x10000) >> 10);
|
||||
/* low surrogate */
|
||||
else
|
||||
return 0xdc00 + ((rune - 0x10000) & 0x3ff);
|
||||
}
|
||||
return rune;
|
||||
}
|
||||
|
||||
int js_utflen(const char *s)
|
||||
{
|
||||
int c;
|
||||
int n;
|
||||
Rune rune;
|
||||
|
||||
n = 0;
|
||||
for(;;) {
|
||||
c = *(unsigned char *)s;
|
||||
if (c < Runeself) {
|
||||
if (c == 0)
|
||||
return n;
|
||||
s++;
|
||||
n++;
|
||||
} else {
|
||||
s += chartorune(&rune, s);
|
||||
if (rune >= 0x10000)
|
||||
n += 2;
|
||||
else
|
||||
n++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int js_utfptrtoidx(const char *s, const char *p)
|
||||
{
|
||||
Rune rune;
|
||||
int i = 0;
|
||||
while (s < p) {
|
||||
if (*(unsigned char *)s < Runeself)
|
||||
++s;
|
||||
else
|
||||
s += chartorune(&rune, s);
|
||||
if (rune >= 0x10000)
|
||||
i += 2;
|
||||
else
|
||||
i += 1;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
static void jsB_new_String(js_State *J)
|
||||
{
|
||||
js_newstring(J, js_gettop(J) > 1 ? js_tostring(J, 1) : "");
|
||||
}
|
||||
|
||||
static void jsB_String(js_State *J)
|
||||
{
|
||||
js_pushstring(J, js_gettop(J) > 1 ? js_tostring(J, 1) : "");
|
||||
}
|
||||
|
||||
static void Sp_toString(js_State *J)
|
||||
{
|
||||
js_Object *self = js_toobject(J, 0);
|
||||
if (self->type != JS_CSTRING) js_typeerror(J, "not a string");
|
||||
js_pushstring(J, self->u.s.string);
|
||||
}
|
||||
|
||||
static void Sp_valueOf(js_State *J)
|
||||
{
|
||||
js_Object *self = js_toobject(J, 0);
|
||||
if (self->type != JS_CSTRING) js_typeerror(J, "not a string");
|
||||
js_pushstring(J, self->u.s.string);
|
||||
}
|
||||
|
||||
static void Sp_charAt(js_State *J)
|
||||
{
|
||||
char buf[UTFmax + 1];
|
||||
const char *s = checkstring(J, 0);
|
||||
int pos = js_tointeger(J, 1);
|
||||
Rune rune = js_runeat(J, s, pos);
|
||||
if (rune >= 0) {
|
||||
buf[runetochar(buf, &rune)] = 0;
|
||||
js_pushstring(J, buf);
|
||||
} else {
|
||||
js_pushliteral(J, "");
|
||||
}
|
||||
}
|
||||
|
||||
static void Sp_charCodeAt(js_State *J)
|
||||
{
|
||||
const char *s = checkstring(J, 0);
|
||||
int pos = js_tointeger(J, 1);
|
||||
Rune rune = js_runeat(J, s, pos);
|
||||
if (rune >= 0)
|
||||
js_pushnumber(J, rune);
|
||||
else
|
||||
js_pushnumber(J, NAN);
|
||||
}
|
||||
|
||||
static void Sp_concat(js_State *J)
|
||||
{
|
||||
int i, top = js_gettop(J);
|
||||
int n;
|
||||
char * volatile out = NULL;
|
||||
const char *s;
|
||||
|
||||
if (top == 1)
|
||||
return;
|
||||
|
||||
s = checkstring(J, 0);
|
||||
n = 1 + strlen(s);
|
||||
|
||||
if (js_try(J)) {
|
||||
js_free(J, out);
|
||||
js_throw(J);
|
||||
}
|
||||
|
||||
if (n > JS_STRLIMIT)
|
||||
js_rangeerror(J, "invalid string length");
|
||||
out = js_malloc(J, n);
|
||||
strcpy(out, s);
|
||||
|
||||
for (i = 1; i < top; ++i) {
|
||||
s = js_tostring(J, i);
|
||||
n += strlen(s);
|
||||
if (n > JS_STRLIMIT)
|
||||
js_rangeerror(J, "invalid string length");
|
||||
out = js_realloc(J, out, n);
|
||||
strcat(out, s);
|
||||
}
|
||||
|
||||
js_pushstring(J, out);
|
||||
js_endtry(J);
|
||||
js_free(J, out);
|
||||
}
|
||||
|
||||
static void Sp_indexOf(js_State *J)
|
||||
{
|
||||
const char *haystack = checkstring(J, 0);
|
||||
const char *needle = js_tostring(J, 1);
|
||||
int pos = js_tointeger(J, 2);
|
||||
int len = strlen(needle);
|
||||
int k = 0;
|
||||
Rune rune;
|
||||
while (*haystack) {
|
||||
if (k >= pos && !strncmp(haystack, needle, len)) {
|
||||
js_pushnumber(J, k);
|
||||
return;
|
||||
}
|
||||
haystack += chartorune(&rune, haystack);
|
||||
++k;
|
||||
}
|
||||
js_pushnumber(J, -1);
|
||||
}
|
||||
|
||||
static void Sp_lastIndexOf(js_State *J)
|
||||
{
|
||||
const char *haystack = checkstring(J, 0);
|
||||
const char *needle = js_tostring(J, 1);
|
||||
int pos = js_isdefined(J, 2) ? js_tointeger(J, 2) : (int)strlen(haystack);
|
||||
int len = strlen(needle);
|
||||
int k = 0, last = -1;
|
||||
Rune rune;
|
||||
while (*haystack && k <= pos) {
|
||||
if (!strncmp(haystack, needle, len))
|
||||
last = k;
|
||||
haystack += chartorune(&rune, haystack);
|
||||
++k;
|
||||
}
|
||||
js_pushnumber(J, last);
|
||||
}
|
||||
|
||||
static void Sp_localeCompare(js_State *J)
|
||||
{
|
||||
const char *a = checkstring(J, 0);
|
||||
const char *b = js_tostring(J, 1);
|
||||
js_pushnumber(J, strcmp(a, b));
|
||||
}
|
||||
|
||||
static void Sp_substring_imp(js_State *J, const char *s, int a, int n)
|
||||
{
|
||||
Rune head_rune = 0, tail_rune = 0;
|
||||
const char *head, *tail;
|
||||
char *p;
|
||||
int i, k, head_len, tail_len;
|
||||
|
||||
/* find start of substring */
|
||||
head = s;
|
||||
for (i = 0; i < a; ++i) {
|
||||
head += chartorune(&head_rune, head);
|
||||
if (head_rune >= 0x10000)
|
||||
++i;
|
||||
}
|
||||
|
||||
/* find end of substring */
|
||||
tail = head;
|
||||
for (k = i - a; k < n; ++k) {
|
||||
tail += chartorune(&tail_rune, tail);
|
||||
if (tail_rune >= 0x10000)
|
||||
++k;
|
||||
}
|
||||
|
||||
/* no surrogate pair splits! */
|
||||
if (i == a && k == n) {
|
||||
js_pushlstring(J, head, tail - head);
|
||||
return;
|
||||
}
|
||||
|
||||
if (js_try(J)) {
|
||||
js_free(J, p);
|
||||
js_throw(J);
|
||||
}
|
||||
|
||||
p = js_malloc(J, UTFmax + (tail - head));
|
||||
|
||||
/* substring starts with low surrogate (head is just after character) */
|
||||
if (i > a) {
|
||||
head_rune = 0xdc00 + ((head_rune - 0x10000) & 0x3ff);
|
||||
head_len = runetochar(p, &head_rune);
|
||||
memcpy(p + head_len, head, tail - head);
|
||||
js_pushlstring(J, p, head_len + (tail - head));
|
||||
}
|
||||
|
||||
/* substring ends with high surrogate (tail is just after character) */
|
||||
if (k > n) {
|
||||
tail -= runelen(tail_rune);
|
||||
memcpy(p, head, tail - head);
|
||||
tail_rune = 0xd800 + ((tail_rune - 0x10000) >> 10);
|
||||
tail_len = runetochar(p + (tail - head), &tail_rune);
|
||||
js_pushlstring(J, p, (tail - head) + tail_len);
|
||||
}
|
||||
|
||||
js_endtry(J);
|
||||
js_free(J, p);
|
||||
}
|
||||
|
||||
static void Sp_slice(js_State *J)
|
||||
{
|
||||
const char *str = checkstring(J, 0);
|
||||
int len = js_utflen(str);
|
||||
int s = js_tointeger(J, 1);
|
||||
int e = js_isdefined(J, 2) ? js_tointeger(J, 2) : len;
|
||||
|
||||
s = s < 0 ? s + len : s;
|
||||
e = e < 0 ? e + len : e;
|
||||
|
||||
s = s < 0 ? 0 : s > len ? len : s;
|
||||
e = e < 0 ? 0 : e > len ? len : e;
|
||||
|
||||
if (s < e)
|
||||
Sp_substring_imp(J, str, s, e - s);
|
||||
else
|
||||
Sp_substring_imp(J, str, e, s - e);
|
||||
}
|
||||
|
||||
static void Sp_substring(js_State *J)
|
||||
{
|
||||
const char *str = checkstring(J, 0);
|
||||
int len = js_utflen(str);
|
||||
int s = js_tointeger(J, 1);
|
||||
int e = js_isdefined(J, 2) ? js_tointeger(J, 2) : len;
|
||||
|
||||
s = s < 0 ? 0 : s > len ? len : s;
|
||||
e = e < 0 ? 0 : e > len ? len : e;
|
||||
|
||||
if (s < e)
|
||||
Sp_substring_imp(J, str, s, e - s);
|
||||
else
|
||||
Sp_substring_imp(J, str, e, s - e);
|
||||
}
|
||||
|
||||
static void Sp_toLowerCase(js_State *J)
|
||||
{
|
||||
const char *s, *s0 = checkstring(J, 0);
|
||||
char * volatile dst = NULL;
|
||||
char *d;
|
||||
Rune rune;
|
||||
const Rune *full;
|
||||
int n;
|
||||
|
||||
n = 1;
|
||||
for (s = s0; *s;) {
|
||||
s += chartorune(&rune, s);
|
||||
full = tolowerrune_full(rune);
|
||||
if (full) {
|
||||
while (*full) {
|
||||
n += runelen(*full);
|
||||
++full;
|
||||
}
|
||||
} else {
|
||||
rune = tolowerrune(rune);
|
||||
n += runelen(rune);
|
||||
}
|
||||
}
|
||||
|
||||
if (js_try(J)) {
|
||||
js_free(J, dst);
|
||||
js_throw(J);
|
||||
}
|
||||
|
||||
d = dst = js_malloc(J, n);
|
||||
for (s = s0; *s;) {
|
||||
s += chartorune(&rune, s);
|
||||
full = tolowerrune_full(rune);
|
||||
if (full) {
|
||||
while (*full) {
|
||||
d += runetochar(d, full);
|
||||
++full;
|
||||
}
|
||||
} else {
|
||||
rune = tolowerrune(rune);
|
||||
d += runetochar(d, &rune);
|
||||
}
|
||||
}
|
||||
*d = 0;
|
||||
|
||||
js_pushstring(J, dst);
|
||||
js_endtry(J);
|
||||
js_free(J, dst);
|
||||
}
|
||||
|
||||
static void Sp_toUpperCase(js_State *J)
|
||||
{
|
||||
const char *s, *s0 = checkstring(J, 0);
|
||||
char * volatile dst = NULL;
|
||||
char *d;
|
||||
const Rune *full;
|
||||
Rune rune;
|
||||
int n;
|
||||
|
||||
n = 1;
|
||||
for (s = s0; *s;) {
|
||||
s += chartorune(&rune, s);
|
||||
full = toupperrune_full(rune);
|
||||
if (full) {
|
||||
while (*full) {
|
||||
n += runelen(*full);
|
||||
++full;
|
||||
}
|
||||
} else {
|
||||
rune = toupperrune(rune);
|
||||
n += runelen(rune);
|
||||
}
|
||||
}
|
||||
|
||||
if (js_try(J)) {
|
||||
js_free(J, dst);
|
||||
js_throw(J);
|
||||
}
|
||||
|
||||
d = dst = js_malloc(J, n);
|
||||
for (s = s0; *s;) {
|
||||
s += chartorune(&rune, s);
|
||||
full = toupperrune_full(rune);
|
||||
if (full) {
|
||||
while (*full) {
|
||||
d += runetochar(d, full);
|
||||
++full;
|
||||
}
|
||||
} else {
|
||||
rune = toupperrune(rune);
|
||||
d += runetochar(d, &rune);
|
||||
}
|
||||
}
|
||||
*d = 0;
|
||||
|
||||
js_pushstring(J, dst);
|
||||
js_endtry(J);
|
||||
js_free(J, dst);
|
||||
}
|
||||
|
||||
static int istrim(int c)
|
||||
{
|
||||
return c == 0x9 || c == 0xB || c == 0xC || c == 0x20 || c == 0xA0 || c == 0xFEFF ||
|
||||
c == 0xA || c == 0xD || c == 0x2028 || c == 0x2029;
|
||||
}
|
||||
|
||||
static void Sp_trim(js_State *J)
|
||||
{
|
||||
const char *s, *e;
|
||||
s = checkstring(J, 0);
|
||||
while (istrim(*s))
|
||||
++s;
|
||||
e = s + strlen(s);
|
||||
while (e > s && istrim(e[-1]))
|
||||
--e;
|
||||
js_pushlstring(J, s, e - s);
|
||||
}
|
||||
|
||||
static void S_fromCharCode(js_State *J)
|
||||
{
|
||||
int i, top = js_gettop(J);
|
||||
char * volatile s = NULL;
|
||||
char *p;
|
||||
Rune c;
|
||||
|
||||
if (js_try(J)) {
|
||||
js_free(J, s);
|
||||
js_throw(J);
|
||||
}
|
||||
|
||||
s = p = js_malloc(J, (top-1) * UTFmax + 1);
|
||||
|
||||
for (i = 1; i < top; ++i) {
|
||||
c = js_touint32(J, i);
|
||||
p += runetochar(p, &c);
|
||||
}
|
||||
*p = 0;
|
||||
|
||||
js_pushstring(J, s);
|
||||
js_endtry(J);
|
||||
js_free(J, s);
|
||||
}
|
||||
|
||||
static void Sp_match(js_State *J)
|
||||
{
|
||||
js_Regexp *re;
|
||||
const char *text;
|
||||
int len;
|
||||
const char *a, *b, *c, *e;
|
||||
Resub m;
|
||||
|
||||
text = checkstring(J, 0);
|
||||
|
||||
if (js_isregexp(J, 1))
|
||||
js_copy(J, 1);
|
||||
else if (js_isundefined(J, 1))
|
||||
js_newregexp(J, "", 0);
|
||||
else
|
||||
js_newregexp(J, js_tostring(J, 1), 0);
|
||||
|
||||
re = js_toregexp(J, -1);
|
||||
if (!(re->flags & JS_REGEXP_G)) {
|
||||
js_RegExp_prototype_exec(J, re, text);
|
||||
return;
|
||||
}
|
||||
|
||||
re->last = 0;
|
||||
|
||||
js_newarray(J);
|
||||
|
||||
len = 0;
|
||||
a = text;
|
||||
e = text + strlen(text);
|
||||
while (a <= e) {
|
||||
if (js_doregexec(J, re->prog, a, &m, a > text ? REG_NOTBOL : 0))
|
||||
break;
|
||||
|
||||
b = m.sub[0].sp;
|
||||
c = m.sub[0].ep;
|
||||
|
||||
js_pushlstring(J, b, c - b);
|
||||
js_setindex(J, -2, len++);
|
||||
|
||||
a = c;
|
||||
if (c - b == 0)
|
||||
++a;
|
||||
}
|
||||
|
||||
if (len == 0) {
|
||||
js_pop(J, 1);
|
||||
js_pushnull(J);
|
||||
}
|
||||
}
|
||||
|
||||
static void Sp_search(js_State *J)
|
||||
{
|
||||
js_Regexp *re;
|
||||
const char *text;
|
||||
Resub m;
|
||||
|
||||
text = checkstring(J, 0);
|
||||
|
||||
if (js_isregexp(J, 1))
|
||||
js_copy(J, 1);
|
||||
else if (js_isundefined(J, 1))
|
||||
js_newregexp(J, "", 0);
|
||||
else
|
||||
js_newregexp(J, js_tostring(J, 1), 0);
|
||||
|
||||
re = js_toregexp(J, -1);
|
||||
|
||||
if (!js_doregexec(J, re->prog, text, &m, 0))
|
||||
js_pushnumber(J, js_utfptrtoidx(text, m.sub[0].sp));
|
||||
else
|
||||
js_pushnumber(J, -1);
|
||||
}
|
||||
|
||||
static void Sp_replace_regexp(js_State *J)
|
||||
{
|
||||
js_Regexp *re;
|
||||
const char *source, *s, *r;
|
||||
js_Buffer *sb = NULL;
|
||||
int n, x;
|
||||
Resub m;
|
||||
|
||||
source = checkstring(J, 0);
|
||||
re = js_toregexp(J, 1);
|
||||
|
||||
if (js_doregexec(J, re->prog, source, &m, 0)) {
|
||||
js_copy(J, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
re->last = 0;
|
||||
|
||||
loop:
|
||||
s = m.sub[0].sp;
|
||||
n = m.sub[0].ep - m.sub[0].sp;
|
||||
|
||||
if (js_iscallable(J, 2)) {
|
||||
js_copy(J, 2);
|
||||
js_pushundefined(J);
|
||||
for (x = 0; m.sub[x].sp; ++x) /* arg 0..x: substring and subexps that matched */
|
||||
js_pushlstring(J, m.sub[x].sp, m.sub[x].ep - m.sub[x].sp);
|
||||
js_pushnumber(J, s - source); /* arg x+2: offset within search string */
|
||||
js_copy(J, 0); /* arg x+3: search string */
|
||||
js_call(J, 2 + x);
|
||||
r = js_tostring(J, -1);
|
||||
js_putm(J, &sb, source, s);
|
||||
js_puts(J, &sb, r);
|
||||
js_pop(J, 1);
|
||||
} else {
|
||||
r = js_tostring(J, 2);
|
||||
js_putm(J, &sb, source, s);
|
||||
while (*r) {
|
||||
if (*r == '$') {
|
||||
switch (*(++r)) {
|
||||
case 0: --r; /* end of string; back up */
|
||||
/* fallthrough */
|
||||
case '$': js_putc(J, &sb, '$'); break;
|
||||
case '`': js_putm(J, &sb, source, s); break;
|
||||
case '\'': js_puts(J, &sb, s + n); break;
|
||||
case '&':
|
||||
js_putm(J, &sb, s, s + n);
|
||||
break;
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
x = *r - '0';
|
||||
if (r[1] >= '0' && r[1] <= '9')
|
||||
x = x * 10 + *(++r) - '0';
|
||||
if (x > 0 && x < m.nsub) {
|
||||
js_putm(J, &sb, m.sub[x].sp, m.sub[x].ep);
|
||||
} else {
|
||||
js_putc(J, &sb, '$');
|
||||
if (x > 10) {
|
||||
js_putc(J, &sb, '0' + x / 10);
|
||||
js_putc(J, &sb, '0' + x % 10);
|
||||
} else {
|
||||
js_putc(J, &sb, '0' + x);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
js_putc(J, &sb, '$');
|
||||
js_putc(J, &sb, *r);
|
||||
break;
|
||||
}
|
||||
++r;
|
||||
} else {
|
||||
js_putc(J, &sb, *r++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (re->flags & JS_REGEXP_G) {
|
||||
source = m.sub[0].ep;
|
||||
if (n == 0) {
|
||||
if (*source)
|
||||
js_putc(J, &sb, *source++);
|
||||
else
|
||||
goto end;
|
||||
}
|
||||
if (!js_doregexec(J, re->prog, source, &m, REG_NOTBOL))
|
||||
goto loop;
|
||||
}
|
||||
|
||||
end:
|
||||
js_puts(J, &sb, s + n);
|
||||
js_putc(J, &sb, 0);
|
||||
|
||||
if (js_try(J)) {
|
||||
js_free(J, sb);
|
||||
js_throw(J);
|
||||
}
|
||||
js_pushstring(J, sb ? sb->s : "");
|
||||
js_endtry(J);
|
||||
js_free(J, sb);
|
||||
}
|
||||
|
||||
static void Sp_replace_string(js_State *J)
|
||||
{
|
||||
const char *source, *needle, *s, *r;
|
||||
js_Buffer *sb = NULL;
|
||||
int n;
|
||||
|
||||
source = checkstring(J, 0);
|
||||
needle = js_tostring(J, 1);
|
||||
|
||||
s = strstr(source, needle);
|
||||
if (!s) {
|
||||
js_copy(J, 0);
|
||||
return;
|
||||
}
|
||||
n = strlen(needle);
|
||||
|
||||
if (js_iscallable(J, 2)) {
|
||||
js_copy(J, 2);
|
||||
js_pushundefined(J);
|
||||
js_pushlstring(J, s, n); /* arg 1: substring that matched */
|
||||
js_pushnumber(J, s - source); /* arg 2: offset within search string */
|
||||
js_copy(J, 0); /* arg 3: search string */
|
||||
js_call(J, 3);
|
||||
r = js_tostring(J, -1);
|
||||
js_putm(J, &sb, source, s);
|
||||
js_puts(J, &sb, r);
|
||||
js_puts(J, &sb, s + n);
|
||||
js_putc(J, &sb, 0);
|
||||
js_pop(J, 1);
|
||||
} else {
|
||||
r = js_tostring(J, 2);
|
||||
js_putm(J, &sb, source, s);
|
||||
while (*r) {
|
||||
if (*r == '$') {
|
||||
switch (*(++r)) {
|
||||
case 0: --r; /* end of string; back up */
|
||||
/* fallthrough */
|
||||
case '$': js_putc(J, &sb, '$'); break;
|
||||
case '&': js_putm(J, &sb, s, s + n); break;
|
||||
case '`': js_putm(J, &sb, source, s); break;
|
||||
case '\'': js_puts(J, &sb, s + n); break;
|
||||
default: js_putc(J, &sb, '$'); js_putc(J, &sb, *r); break;
|
||||
}
|
||||
++r;
|
||||
} else {
|
||||
js_putc(J, &sb, *r++);
|
||||
}
|
||||
}
|
||||
js_puts(J, &sb, s + n);
|
||||
js_putc(J, &sb, 0);
|
||||
}
|
||||
|
||||
if (js_try(J)) {
|
||||
js_free(J, sb);
|
||||
js_throw(J);
|
||||
}
|
||||
js_pushstring(J, sb ? sb->s : "");
|
||||
js_endtry(J);
|
||||
js_free(J, sb);
|
||||
}
|
||||
|
||||
static void Sp_replace(js_State *J)
|
||||
{
|
||||
if (js_isregexp(J, 1))
|
||||
Sp_replace_regexp(J);
|
||||
else
|
||||
Sp_replace_string(J);
|
||||
}
|
||||
|
||||
static void Sp_split_regexp(js_State *J)
|
||||
{
|
||||
js_Regexp *re;
|
||||
const char *text;
|
||||
int limit, len, k;
|
||||
const char *p, *a, *b, *c, *e;
|
||||
Resub m;
|
||||
|
||||
text = checkstring(J, 0);
|
||||
re = js_toregexp(J, 1);
|
||||
limit = js_isdefined(J, 2) ? js_tointeger(J, 2) : 1 << 30;
|
||||
|
||||
js_newarray(J);
|
||||
len = 0;
|
||||
|
||||
if (limit == 0)
|
||||
return;
|
||||
|
||||
e = text + strlen(text);
|
||||
|
||||
/* splitting the empty string */
|
||||
if (e == text) {
|
||||
if (js_doregexec(J, re->prog, text, &m, 0)) {
|
||||
js_pushliteral(J, "");
|
||||
js_setindex(J, -2, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
p = a = text;
|
||||
while (a < e) {
|
||||
if (js_doregexec(J, re->prog, a, &m, a > text ? REG_NOTBOL : 0))
|
||||
break; /* no match */
|
||||
|
||||
b = m.sub[0].sp;
|
||||
c = m.sub[0].ep;
|
||||
|
||||
/* empty string at end of last match */
|
||||
if (b == c && b == p) {
|
||||
++a;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (len == limit) return;
|
||||
js_pushlstring(J, p, b - p);
|
||||
js_setindex(J, -2, len++);
|
||||
|
||||
for (k = 1; k < m.nsub; ++k) {
|
||||
if (len == limit) return;
|
||||
js_pushlstring(J, m.sub[k].sp, m.sub[k].ep - m.sub[k].sp);
|
||||
js_setindex(J, -2, len++);
|
||||
}
|
||||
|
||||
a = p = c;
|
||||
}
|
||||
|
||||
if (len == limit) return;
|
||||
js_pushstring(J, p);
|
||||
js_setindex(J, -2, len);
|
||||
}
|
||||
|
||||
static void Sp_split_string(js_State *J)
|
||||
{
|
||||
const char *str = checkstring(J, 0);
|
||||
const char *sep = js_tostring(J, 1);
|
||||
int limit = js_isdefined(J, 2) ? js_tointeger(J, 2) : 1 << 30;
|
||||
int i, n;
|
||||
|
||||
js_newarray(J);
|
||||
|
||||
if (limit == 0)
|
||||
return;
|
||||
|
||||
n = strlen(sep);
|
||||
|
||||
/* empty string */
|
||||
if (n == 0) {
|
||||
Rune rune;
|
||||
for (i = 0; *str && i < limit; ++i) {
|
||||
n = chartorune(&rune, str);
|
||||
js_pushlstring(J, str, n);
|
||||
js_setindex(J, -2, i);
|
||||
str += n;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; str && i < limit; ++i) {
|
||||
const char *s = strstr(str, sep);
|
||||
if (s) {
|
||||
js_pushlstring(J, str, s-str);
|
||||
js_setindex(J, -2, i);
|
||||
str = s + n;
|
||||
} else {
|
||||
js_pushstring(J, str);
|
||||
js_setindex(J, -2, i);
|
||||
str = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Sp_split(js_State *J)
|
||||
{
|
||||
if (js_isundefined(J, 1)) {
|
||||
js_newarray(J);
|
||||
js_pushstring(J, js_tostring(J, 0));
|
||||
js_setindex(J, -2, 0);
|
||||
} else if (js_isregexp(J, 1)) {
|
||||
Sp_split_regexp(J);
|
||||
} else {
|
||||
Sp_split_string(J);
|
||||
}
|
||||
}
|
||||
|
||||
void jsB_initstring(js_State *J)
|
||||
{
|
||||
J->String_prototype->u.s.shrstr[0] = 0;
|
||||
J->String_prototype->u.s.string = J->String_prototype->u.s.shrstr;
|
||||
J->String_prototype->u.s.length = 0;
|
||||
|
||||
js_pushobject(J, J->String_prototype);
|
||||
{
|
||||
jsB_propf(J, "String.prototype.toString", Sp_toString, 0);
|
||||
jsB_propf(J, "String.prototype.valueOf", Sp_valueOf, 0);
|
||||
jsB_propf(J, "String.prototype.charAt", Sp_charAt, 1);
|
||||
jsB_propf(J, "String.prototype.charCodeAt", Sp_charCodeAt, 1);
|
||||
jsB_propf(J, "String.prototype.concat", Sp_concat, 0); /* 1 */
|
||||
jsB_propf(J, "String.prototype.indexOf", Sp_indexOf, 1);
|
||||
jsB_propf(J, "String.prototype.lastIndexOf", Sp_lastIndexOf, 1);
|
||||
jsB_propf(J, "String.prototype.localeCompare", Sp_localeCompare, 1);
|
||||
jsB_propf(J, "String.prototype.match", Sp_match, 1);
|
||||
jsB_propf(J, "String.prototype.replace", Sp_replace, 2);
|
||||
jsB_propf(J, "String.prototype.search", Sp_search, 1);
|
||||
jsB_propf(J, "String.prototype.slice", Sp_slice, 2);
|
||||
jsB_propf(J, "String.prototype.split", Sp_split, 2);
|
||||
jsB_propf(J, "String.prototype.substring", Sp_substring, 2);
|
||||
jsB_propf(J, "String.prototype.toLowerCase", Sp_toLowerCase, 0);
|
||||
jsB_propf(J, "String.prototype.toLocaleLowerCase", Sp_toLowerCase, 0);
|
||||
jsB_propf(J, "String.prototype.toUpperCase", Sp_toUpperCase, 0);
|
||||
jsB_propf(J, "String.prototype.toLocaleUpperCase", Sp_toUpperCase, 0);
|
||||
|
||||
/* ES5 */
|
||||
jsB_propf(J, "String.prototype.trim", Sp_trim, 0);
|
||||
}
|
||||
js_newcconstructor(J, jsB_String, jsB_new_String, "String", 0); /* 1 */
|
||||
{
|
||||
jsB_propf(J, "String.fromCharCode", S_fromCharCode, 0); /* 1 */
|
||||
}
|
||||
js_defglobal(J, "String", JS_DONTENUM);
|
||||
}
|
708
src/mujs/jsvalue.c
Normal file
708
src/mujs/jsvalue.c
Normal file
|
@ -0,0 +1,708 @@
|
|||
#include "jsi.h"
|
||||
#include "utf.h"
|
||||
|
||||
#define JSV_ISSTRING(v) (v->t.type==JS_TSHRSTR || v->t.type==JS_TMEMSTR || v->t.type==JS_TLITSTR)
|
||||
#define JSV_TOSTRING(v) (v->t.type==JS_TSHRSTR ? v->u.shrstr : v->t.type==JS_TLITSTR ? v->u.litstr : v->t.type==JS_TMEMSTR ? v->u.memstr->p : "")
|
||||
|
||||
double js_strtol(const char *s, char **p, int base)
|
||||
{
|
||||
/* ascii -> digit value. max base is 36. */
|
||||
static const unsigned char table[256] = {
|
||||
80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
|
||||
80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
|
||||
80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 80, 80, 80, 80, 80, 80,
|
||||
80, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
|
||||
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 80, 80, 80, 80, 80,
|
||||
80, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
|
||||
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 80, 80, 80, 80, 80,
|
||||
80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
|
||||
80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
|
||||
80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
|
||||
80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
|
||||
80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
|
||||
80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
|
||||
80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
|
||||
80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80
|
||||
};
|
||||
double x;
|
||||
unsigned char c;
|
||||
if (base == 10)
|
||||
for (x = 0, c = *s++; (0 <= c - '0') && (c - '0' < 10); c = *s++)
|
||||
x = x * 10 + (c - '0');
|
||||
else
|
||||
for (x = 0, c = *s++; table[c] < base; c = *s++)
|
||||
x = x * base + table[c];
|
||||
if (p)
|
||||
*p = (char*)s-1;
|
||||
return x;
|
||||
}
|
||||
|
||||
int jsV_numbertointeger(double n)
|
||||
{
|
||||
if (n == 0) return 0;
|
||||
if (isnan(n)) return 0;
|
||||
n = (n < 0) ? -floor(-n) : floor(n);
|
||||
if (n < INT_MIN) return INT_MIN;
|
||||
if (n > INT_MAX) return INT_MAX;
|
||||
return (int)n;
|
||||
}
|
||||
|
||||
int jsV_numbertoint32(double n)
|
||||
{
|
||||
double two32 = 4294967296.0;
|
||||
double two31 = 2147483648.0;
|
||||
|
||||
if (!isfinite(n) || n == 0)
|
||||
return 0;
|
||||
|
||||
n = fmod(n, two32);
|
||||
n = n >= 0 ? floor(n) : ceil(n) + two32;
|
||||
if (n >= two31)
|
||||
return n - two32;
|
||||
else
|
||||
return n;
|
||||
}
|
||||
|
||||
unsigned int jsV_numbertouint32(double n)
|
||||
{
|
||||
return (unsigned int)jsV_numbertoint32(n);
|
||||
}
|
||||
|
||||
short jsV_numbertoint16(double n)
|
||||
{
|
||||
return jsV_numbertoint32(n);
|
||||
}
|
||||
|
||||
unsigned short jsV_numbertouint16(double n)
|
||||
{
|
||||
return jsV_numbertoint32(n);
|
||||
}
|
||||
|
||||
/* obj.toString() */
|
||||
static int jsV_toString(js_State *J, js_Object *obj)
|
||||
{
|
||||
js_pushobject(J, obj);
|
||||
js_getproperty(J, -1, "toString");
|
||||
if (js_iscallable(J, -1)) {
|
||||
js_rot2(J);
|
||||
js_call(J, 0);
|
||||
if (js_isprimitive(J, -1))
|
||||
return 1;
|
||||
js_pop(J, 1);
|
||||
return 0;
|
||||
}
|
||||
js_pop(J, 2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* obj.valueOf() */
|
||||
static int jsV_valueOf(js_State *J, js_Object *obj)
|
||||
{
|
||||
js_pushobject(J, obj);
|
||||
js_getproperty(J, -1, "valueOf");
|
||||
if (js_iscallable(J, -1)) {
|
||||
js_rot2(J);
|
||||
js_call(J, 0);
|
||||
if (js_isprimitive(J, -1))
|
||||
return 1;
|
||||
js_pop(J, 1);
|
||||
return 0;
|
||||
}
|
||||
js_pop(J, 2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ToPrimitive() on a value */
|
||||
void jsV_toprimitive(js_State *J, js_Value *v, int preferred)
|
||||
{
|
||||
js_Object *obj;
|
||||
|
||||
if (v->t.type != JS_TOBJECT)
|
||||
return;
|
||||
|
||||
obj = v->u.object;
|
||||
|
||||
if (preferred == JS_HNONE)
|
||||
preferred = obj->type == JS_CDATE ? JS_HSTRING : JS_HNUMBER;
|
||||
|
||||
if (preferred == JS_HSTRING) {
|
||||
if (jsV_toString(J, obj) || jsV_valueOf(J, obj)) {
|
||||
*v = *js_tovalue(J, -1);
|
||||
js_pop(J, 1);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (jsV_valueOf(J, obj) || jsV_toString(J, obj)) {
|
||||
*v = *js_tovalue(J, -1);
|
||||
js_pop(J, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (J->strict)
|
||||
js_typeerror(J, "cannot convert object to primitive");
|
||||
|
||||
v->t.type = JS_TLITSTR;
|
||||
v->u.litstr = "[object]";
|
||||
return;
|
||||
}
|
||||
|
||||
/* ToBoolean() on a value */
|
||||
int jsV_toboolean(js_State *J, js_Value *v)
|
||||
{
|
||||
switch (v->t.type) {
|
||||
default:
|
||||
case JS_TSHRSTR: return v->u.shrstr[0] != 0;
|
||||
case JS_TUNDEFINED: return 0;
|
||||
case JS_TNULL: return 0;
|
||||
case JS_TBOOLEAN: return v->u.boolean;
|
||||
case JS_TNUMBER: return v->u.number != 0 && !isnan(v->u.number);
|
||||
case JS_TLITSTR: return v->u.litstr[0] != 0;
|
||||
case JS_TMEMSTR: return v->u.memstr->p[0] != 0;
|
||||
case JS_TOBJECT: return 1;
|
||||
}
|
||||
}
|
||||
|
||||
const char *js_itoa(char *out, int v)
|
||||
{
|
||||
char buf[32], *s = out;
|
||||
unsigned int a;
|
||||
int i = 0;
|
||||
if (v < 0) {
|
||||
a = -v;
|
||||
*s++ = '-';
|
||||
} else {
|
||||
a = v;
|
||||
}
|
||||
while (a) {
|
||||
buf[i++] = (a % 10) + '0';
|
||||
a /= 10;
|
||||
}
|
||||
if (i == 0)
|
||||
buf[i++] = '0';
|
||||
while (i > 0)
|
||||
*s++ = buf[--i];
|
||||
*s = 0;
|
||||
return out;
|
||||
}
|
||||
|
||||
double js_stringtofloat(const char *s, char **ep)
|
||||
{
|
||||
char *end;
|
||||
double n;
|
||||
const char *e = s;
|
||||
int isflt = 0;
|
||||
if (*e == '+' || *e == '-') ++e;
|
||||
while (*e >= '0' && *e <= '9') ++e;
|
||||
if (*e == '.') { ++e; isflt = 1; }
|
||||
while (*e >= '0' && *e <= '9') ++e;
|
||||
if (*e == 'e' || *e == 'E') {
|
||||
++e;
|
||||
if (*e == '+' || *e == '-') ++e;
|
||||
while (*e >= '0' && *e <= '9') ++e;
|
||||
isflt = 1;
|
||||
}
|
||||
if (isflt)
|
||||
n = js_strtod(s, &end);
|
||||
else {
|
||||
/* js_strtol doesn't parse the sign */
|
||||
if (*s == '-')
|
||||
n = -js_strtol(s+1, &end, 10);
|
||||
else if (*s == '+')
|
||||
n = js_strtol(s+1, &end, 10);
|
||||
else
|
||||
n = js_strtol(s, &end, 10);
|
||||
}
|
||||
if (end == e) {
|
||||
*ep = (char*)e;
|
||||
return n;
|
||||
}
|
||||
*ep = (char*)s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ToNumber() on a string */
|
||||
double jsV_stringtonumber(js_State *J, const char *s)
|
||||
{
|
||||
char *e;
|
||||
double n;
|
||||
while (jsY_iswhite(*s) || jsY_isnewline(*s)) ++s;
|
||||
if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X') && s[2] != 0)
|
||||
n = js_strtol(s + 2, &e, 16);
|
||||
else if (!strncmp(s, "Infinity", 8))
|
||||
n = INFINITY, e = (char*)s + 8;
|
||||
else if (!strncmp(s, "+Infinity", 9))
|
||||
n = INFINITY, e = (char*)s + 9;
|
||||
else if (!strncmp(s, "-Infinity", 9))
|
||||
n = -INFINITY, e = (char*)s + 9;
|
||||
else
|
||||
n = js_stringtofloat(s, &e);
|
||||
while (jsY_iswhite(*e) || jsY_isnewline(*e)) ++e;
|
||||
if (*e) return NAN;
|
||||
return n;
|
||||
}
|
||||
|
||||
/* ToNumber() on a value */
|
||||
double jsV_tonumber(js_State *J, js_Value *v)
|
||||
{
|
||||
switch (v->t.type) {
|
||||
default:
|
||||
case JS_TSHRSTR: return jsV_stringtonumber(J, v->u.shrstr);
|
||||
case JS_TUNDEFINED: return NAN;
|
||||
case JS_TNULL: return 0;
|
||||
case JS_TBOOLEAN: return v->u.boolean;
|
||||
case JS_TNUMBER: return v->u.number;
|
||||
case JS_TLITSTR: return jsV_stringtonumber(J, v->u.litstr);
|
||||
case JS_TMEMSTR: return jsV_stringtonumber(J, v->u.memstr->p);
|
||||
case JS_TOBJECT:
|
||||
jsV_toprimitive(J, v, JS_HNUMBER);
|
||||
return jsV_tonumber(J, v);
|
||||
}
|
||||
}
|
||||
|
||||
double jsV_tointeger(js_State *J, js_Value *v)
|
||||
{
|
||||
return jsV_numbertointeger(jsV_tonumber(J, v));
|
||||
}
|
||||
|
||||
/* ToString() on a number */
|
||||
const char *jsV_numbertostring(js_State *J, char buf[32], double f)
|
||||
{
|
||||
char digits[32], *p = buf, *s = digits;
|
||||
int exp, ndigits, point;
|
||||
|
||||
if (f == 0) return "0";
|
||||
if (isnan(f)) return "NaN";
|
||||
if (isinf(f)) return f < 0 ? "-Infinity" : "Infinity";
|
||||
|
||||
/* Fast case for integers. This only works assuming all integers can be
|
||||
* exactly represented by a float. This is true for 32-bit integers and
|
||||
* 64-bit floats. */
|
||||
if (f >= INT_MIN && f <= INT_MAX) {
|
||||
int i = (int)f;
|
||||
if ((double)i == f)
|
||||
return js_itoa(buf, i);
|
||||
}
|
||||
|
||||
ndigits = js_grisu2(f, digits, &exp);
|
||||
point = ndigits + exp;
|
||||
|
||||
if (signbit(f))
|
||||
*p++ = '-';
|
||||
|
||||
if (point < -5 || point > 21) {
|
||||
*p++ = *s++;
|
||||
if (ndigits > 1) {
|
||||
int n = ndigits - 1;
|
||||
*p++ = '.';
|
||||
while (n--)
|
||||
*p++ = *s++;
|
||||
}
|
||||
js_fmtexp(p, point - 1);
|
||||
}
|
||||
|
||||
else if (point <= 0) {
|
||||
*p++ = '0';
|
||||
*p++ = '.';
|
||||
while (point++ < 0)
|
||||
*p++ = '0';
|
||||
while (ndigits-- > 0)
|
||||
*p++ = *s++;
|
||||
*p = 0;
|
||||
}
|
||||
|
||||
else {
|
||||
while (ndigits-- > 0) {
|
||||
*p++ = *s++;
|
||||
if (--point == 0 && ndigits > 0)
|
||||
*p++ = '.';
|
||||
}
|
||||
while (point-- > 0)
|
||||
*p++ = '0';
|
||||
*p = 0;
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* ToString() on a value */
|
||||
const char *jsV_tostring(js_State *J, js_Value *v)
|
||||
{
|
||||
char buf[32];
|
||||
const char *p;
|
||||
switch (v->t.type) {
|
||||
default:
|
||||
case JS_TSHRSTR: return v->u.shrstr;
|
||||
case JS_TUNDEFINED: return "undefined";
|
||||
case JS_TNULL: return "null";
|
||||
case JS_TBOOLEAN: return v->u.boolean ? "true" : "false";
|
||||
case JS_TLITSTR: return v->u.litstr;
|
||||
case JS_TMEMSTR: return v->u.memstr->p;
|
||||
case JS_TNUMBER:
|
||||
p = jsV_numbertostring(J, buf, v->u.number);
|
||||
if (p == buf) {
|
||||
int n = strlen(p);
|
||||
if (n <= soffsetof(js_Value, t.type)) {
|
||||
char *s = v->u.shrstr;
|
||||
while (n--) *s++ = *p++;
|
||||
*s = 0;
|
||||
v->t.type = JS_TSHRSTR;
|
||||
return v->u.shrstr;
|
||||
} else {
|
||||
v->u.memstr = jsV_newmemstring(J, p, n);
|
||||
v->t.type = JS_TMEMSTR;
|
||||
return v->u.memstr->p;
|
||||
}
|
||||
}
|
||||
return p;
|
||||
case JS_TOBJECT:
|
||||
jsV_toprimitive(J, v, JS_HSTRING);
|
||||
return jsV_tostring(J, v);
|
||||
}
|
||||
}
|
||||
|
||||
/* Objects */
|
||||
|
||||
static js_Object *jsV_newboolean(js_State *J, int v)
|
||||
{
|
||||
js_Object *obj = jsV_newobject(J, JS_CBOOLEAN, J->Boolean_prototype);
|
||||
obj->u.boolean = v;
|
||||
return obj;
|
||||
}
|
||||
|
||||
static js_Object *jsV_newnumber(js_State *J, double v)
|
||||
{
|
||||
js_Object *obj = jsV_newobject(J, JS_CNUMBER, J->Number_prototype);
|
||||
obj->u.number = v;
|
||||
return obj;
|
||||
}
|
||||
|
||||
static js_Object *jsV_newstring(js_State *J, const char *v)
|
||||
{
|
||||
js_Object *obj = jsV_newobject(J, JS_CSTRING, J->String_prototype);
|
||||
size_t n = strlen(v);
|
||||
if (n < sizeof(obj->u.s.shrstr)) {
|
||||
obj->u.s.string = obj->u.s.shrstr;
|
||||
memcpy(obj->u.s.shrstr, v, n + 1);
|
||||
} else {
|
||||
obj->u.s.string = js_strdup(J, v);
|
||||
}
|
||||
obj->u.s.length = js_utflen(v);
|
||||
return obj;
|
||||
}
|
||||
|
||||
/* ToObject() on a value */
|
||||
js_Object *jsV_toobject(js_State *J, js_Value *v)
|
||||
{
|
||||
js_Object *o;
|
||||
switch (v->t.type) {
|
||||
default:
|
||||
case JS_TUNDEFINED: js_typeerror(J, "cannot convert undefined to object");
|
||||
case JS_TNULL: js_typeerror(J, "cannot convert null to object");
|
||||
case JS_TOBJECT: return v->u.object;
|
||||
case JS_TSHRSTR: o = jsV_newstring(J, v->u.shrstr); break;
|
||||
case JS_TLITSTR: o = jsV_newstring(J, v->u.litstr); break;
|
||||
case JS_TMEMSTR: o = jsV_newstring(J, v->u.memstr->p); break;
|
||||
case JS_TBOOLEAN: o = jsV_newboolean(J, v->u.boolean); break;
|
||||
case JS_TNUMBER: o = jsV_newnumber(J, v->u.number); break;
|
||||
}
|
||||
v->t.type = JS_TOBJECT;
|
||||
v->u.object = o;
|
||||
return o;
|
||||
}
|
||||
|
||||
void js_newobjectx(js_State *J)
|
||||
{
|
||||
js_Object *prototype = NULL;
|
||||
if (js_isobject(J, -1))
|
||||
prototype = js_toobject(J, -1);
|
||||
js_pop(J, 1);
|
||||
js_pushobject(J, jsV_newobject(J, JS_COBJECT, prototype));
|
||||
}
|
||||
|
||||
void js_newobject(js_State *J)
|
||||
{
|
||||
js_pushobject(J, jsV_newobject(J, JS_COBJECT, J->Object_prototype));
|
||||
}
|
||||
|
||||
void js_newarguments(js_State *J)
|
||||
{
|
||||
js_pushobject(J, jsV_newobject(J, JS_CARGUMENTS, J->Object_prototype));
|
||||
}
|
||||
|
||||
void js_newarray(js_State *J)
|
||||
{
|
||||
js_Object *obj = jsV_newobject(J, JS_CARRAY, J->Array_prototype);
|
||||
obj->u.a.simple = 1;
|
||||
js_pushobject(J, obj);
|
||||
}
|
||||
|
||||
void js_newboolean(js_State *J, int v)
|
||||
{
|
||||
js_pushobject(J, jsV_newboolean(J, v));
|
||||
}
|
||||
|
||||
void js_newnumber(js_State *J, double v)
|
||||
{
|
||||
js_pushobject(J, jsV_newnumber(J, v));
|
||||
}
|
||||
|
||||
void js_newstring(js_State *J, const char *v)
|
||||
{
|
||||
js_pushobject(J, jsV_newstring(J, v));
|
||||
}
|
||||
|
||||
void js_newfunction(js_State *J, js_Function *fun, js_Environment *scope)
|
||||
{
|
||||
js_Object *obj = jsV_newobject(J, JS_CFUNCTION, J->Function_prototype);
|
||||
obj->u.f.function = fun;
|
||||
obj->u.f.scope = scope;
|
||||
js_pushobject(J, obj);
|
||||
{
|
||||
js_pushnumber(J, fun->numparams);
|
||||
js_defproperty(J, -2, "length", JS_READONLY | JS_DONTENUM | JS_DONTCONF);
|
||||
js_newobject(J);
|
||||
{
|
||||
js_copy(J, -2);
|
||||
js_defproperty(J, -2, "constructor", JS_DONTENUM);
|
||||
}
|
||||
js_defproperty(J, -2, "prototype", JS_DONTENUM | JS_DONTCONF);
|
||||
}
|
||||
}
|
||||
|
||||
void js_newscript(js_State *J, js_Function *fun, js_Environment *scope)
|
||||
{
|
||||
js_Object *obj = jsV_newobject(J, JS_CSCRIPT, NULL);
|
||||
obj->u.f.function = fun;
|
||||
obj->u.f.scope = scope;
|
||||
js_pushobject(J, obj);
|
||||
}
|
||||
|
||||
void js_newcfunctionx(js_State *J, js_CFunction cfun, const char *name, int length, void *data, js_Finalize finalize)
|
||||
{
|
||||
js_Object *obj;
|
||||
|
||||
if (js_try(J)) {
|
||||
if (finalize)
|
||||
finalize(J, data);
|
||||
js_throw(J);
|
||||
}
|
||||
|
||||
obj = jsV_newobject(J, JS_CCFUNCTION, J->Function_prototype);
|
||||
obj->u.c.name = name;
|
||||
obj->u.c.function = cfun;
|
||||
obj->u.c.constructor = NULL;
|
||||
obj->u.c.length = length;
|
||||
obj->u.c.data = data;
|
||||
obj->u.c.finalize = finalize;
|
||||
|
||||
js_endtry(J);
|
||||
|
||||
js_pushobject(J, obj);
|
||||
{
|
||||
js_pushnumber(J, length);
|
||||
js_defproperty(J, -2, "length", JS_READONLY | JS_DONTENUM | JS_DONTCONF);
|
||||
js_newobject(J);
|
||||
{
|
||||
js_copy(J, -2);
|
||||
js_defproperty(J, -2, "constructor", JS_DONTENUM);
|
||||
}
|
||||
js_defproperty(J, -2, "prototype", JS_DONTENUM | JS_DONTCONF);
|
||||
}
|
||||
}
|
||||
|
||||
void js_newcfunction(js_State *J, js_CFunction cfun, const char *name, int length)
|
||||
{
|
||||
js_newcfunctionx(J, cfun, name, length, NULL, NULL);
|
||||
}
|
||||
|
||||
/* prototype -- constructor */
|
||||
void js_newcconstructor(js_State *J, js_CFunction cfun, js_CFunction ccon, const char *name, int length)
|
||||
{
|
||||
js_Object *obj = jsV_newobject(J, JS_CCFUNCTION, J->Function_prototype);
|
||||
obj->u.c.name = name;
|
||||
obj->u.c.function = cfun;
|
||||
obj->u.c.constructor = ccon;
|
||||
obj->u.c.length = length;
|
||||
js_pushobject(J, obj); /* proto obj */
|
||||
{
|
||||
js_pushnumber(J, length);
|
||||
js_defproperty(J, -2, "length", JS_READONLY | JS_DONTENUM | JS_DONTCONF);
|
||||
js_rot2(J); /* obj proto */
|
||||
js_copy(J, -2); /* obj proto obj */
|
||||
js_defproperty(J, -2, "constructor", JS_DONTENUM);
|
||||
js_defproperty(J, -2, "prototype", JS_DONTENUM | JS_DONTCONF);
|
||||
}
|
||||
}
|
||||
|
||||
void js_newuserdatax(js_State *J, const char *tag, void *data, js_HasProperty has, js_Put put, js_Delete delete, js_Finalize finalize)
|
||||
{
|
||||
js_Object *prototype = NULL;
|
||||
js_Object *obj;
|
||||
|
||||
if (js_isobject(J, -1))
|
||||
prototype = js_toobject(J, -1);
|
||||
js_pop(J, 1);
|
||||
|
||||
if (js_try(J)) {
|
||||
if (finalize)
|
||||
finalize(J, data);
|
||||
js_throw(J);
|
||||
}
|
||||
|
||||
obj = jsV_newobject(J, JS_CUSERDATA, prototype);
|
||||
obj->u.user.tag = tag;
|
||||
obj->u.user.data = data;
|
||||
obj->u.user.has = has;
|
||||
obj->u.user.put = put;
|
||||
obj->u.user.delete = delete;
|
||||
obj->u.user.finalize = finalize;
|
||||
|
||||
js_endtry(J);
|
||||
|
||||
js_pushobject(J, obj);
|
||||
}
|
||||
|
||||
void js_newuserdata(js_State *J, const char *tag, void *data, js_Finalize finalize)
|
||||
{
|
||||
js_newuserdatax(J, tag, data, NULL, NULL, NULL, finalize);
|
||||
}
|
||||
|
||||
/* Non-trivial operations on values. These are implemented using the stack. */
|
||||
|
||||
int js_instanceof(js_State *J)
|
||||
{
|
||||
js_Object *O, *V;
|
||||
|
||||
if (!js_iscallable(J, -1))
|
||||
js_typeerror(J, "instanceof: invalid operand");
|
||||
|
||||
if (!js_isobject(J, -2))
|
||||
return 0;
|
||||
|
||||
js_getproperty(J, -1, "prototype");
|
||||
if (!js_isobject(J, -1))
|
||||
js_typeerror(J, "instanceof: 'prototype' property is not an object");
|
||||
O = js_toobject(J, -1);
|
||||
js_pop(J, 1);
|
||||
|
||||
V = js_toobject(J, -2);
|
||||
while (V) {
|
||||
V = V->prototype;
|
||||
if (O == V)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void js_concat(js_State *J)
|
||||
{
|
||||
js_toprimitive(J, -2, JS_HNONE);
|
||||
js_toprimitive(J, -1, JS_HNONE);
|
||||
|
||||
if (js_isstring(J, -2) || js_isstring(J, -1)) {
|
||||
const char *sa = js_tostring(J, -2);
|
||||
const char *sb = js_tostring(J, -1);
|
||||
char * volatile sab = NULL;
|
||||
/* TODO: create js_String directly */
|
||||
if (js_try(J)) {
|
||||
js_free(J, sab);
|
||||
js_throw(J);
|
||||
}
|
||||
sab = js_malloc(J, strlen(sa) + strlen(sb) + 1);
|
||||
strcpy(sab, sa);
|
||||
strcat(sab, sb);
|
||||
js_pop(J, 2);
|
||||
js_pushstring(J, sab);
|
||||
js_endtry(J);
|
||||
js_free(J, sab);
|
||||
} else {
|
||||
double x = js_tonumber(J, -2);
|
||||
double y = js_tonumber(J, -1);
|
||||
js_pop(J, 2);
|
||||
js_pushnumber(J, x + y);
|
||||
}
|
||||
}
|
||||
|
||||
int js_compare(js_State *J, int *okay)
|
||||
{
|
||||
js_toprimitive(J, -2, JS_HNUMBER);
|
||||
js_toprimitive(J, -1, JS_HNUMBER);
|
||||
|
||||
*okay = 1;
|
||||
if (js_isstring(J, -2) && js_isstring(J, -1)) {
|
||||
return strcmp(js_tostring(J, -2), js_tostring(J, -1));
|
||||
} else {
|
||||
double x = js_tonumber(J, -2);
|
||||
double y = js_tonumber(J, -1);
|
||||
if (isnan(x) || isnan(y))
|
||||
*okay = 0;
|
||||
return x < y ? -1 : x > y ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
int js_equal(js_State *J)
|
||||
{
|
||||
js_Value *x = js_tovalue(J, -2);
|
||||
js_Value *y = js_tovalue(J, -1);
|
||||
|
||||
retry:
|
||||
if (JSV_ISSTRING(x) && JSV_ISSTRING(y))
|
||||
return !strcmp(JSV_TOSTRING(x), JSV_TOSTRING(y));
|
||||
if (x->t.type == y->t.type) {
|
||||
if (x->t.type == JS_TUNDEFINED) return 1;
|
||||
if (x->t.type == JS_TNULL) return 1;
|
||||
if (x->t.type == JS_TNUMBER) return x->u.number == y->u.number;
|
||||
if (x->t.type == JS_TBOOLEAN) return x->u.boolean == y->u.boolean;
|
||||
if (x->t.type == JS_TOBJECT) return x->u.object == y->u.object;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (x->t.type == JS_TNULL && y->t.type == JS_TUNDEFINED) return 1;
|
||||
if (x->t.type == JS_TUNDEFINED && y->t.type == JS_TNULL) return 1;
|
||||
|
||||
if (x->t.type == JS_TNUMBER && JSV_ISSTRING(y))
|
||||
return x->u.number == jsV_tonumber(J, y);
|
||||
if (JSV_ISSTRING(x) && y->t.type == JS_TNUMBER)
|
||||
return jsV_tonumber(J, x) == y->u.number;
|
||||
|
||||
if (x->t.type == JS_TBOOLEAN) {
|
||||
x->t.type = JS_TNUMBER;
|
||||
x->u.number = x->u.boolean ? 1 : 0;
|
||||
goto retry;
|
||||
}
|
||||
if (y->t.type == JS_TBOOLEAN) {
|
||||
y->t.type = JS_TNUMBER;
|
||||
y->u.number = y->u.boolean ? 1 : 0;
|
||||
goto retry;
|
||||
}
|
||||
if ((JSV_ISSTRING(x) || x->t.type == JS_TNUMBER) && y->t.type == JS_TOBJECT) {
|
||||
jsV_toprimitive(J, y, JS_HNONE);
|
||||
goto retry;
|
||||
}
|
||||
if (x->t.type == JS_TOBJECT && (JSV_ISSTRING(y) || y->t.type == JS_TNUMBER)) {
|
||||
jsV_toprimitive(J, x, JS_HNONE);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int js_strictequal(js_State *J)
|
||||
{
|
||||
js_Value *x = js_tovalue(J, -2);
|
||||
js_Value *y = js_tovalue(J, -1);
|
||||
|
||||
if (JSV_ISSTRING(x) && JSV_ISSTRING(y))
|
||||
return !strcmp(JSV_TOSTRING(x), JSV_TOSTRING(y));
|
||||
|
||||
if (x->t.type != y->t.type) return 0;
|
||||
if (x->t.type == JS_TUNDEFINED) return 1;
|
||||
if (x->t.type == JS_TNULL) return 1;
|
||||
if (x->t.type == JS_TNUMBER) return x->u.number == y->u.number;
|
||||
if (x->t.type == JS_TBOOLEAN) return x->u.boolean == y->u.boolean;
|
||||
if (x->t.type == JS_TOBJECT) return x->u.object == y->u.object;
|
||||
return 0;
|
||||
}
|
400
src/mujs/main.c
Normal file
400
src/mujs/main.c
Normal file
|
@ -0,0 +1,400 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifdef _MSC_VER
|
||||
#include <io.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <errno.h>
|
||||
|
||||
#include "mujs.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "../libtemple/os.h"
|
||||
|
||||
static char *xoptarg; /* Global argument pointer. */
|
||||
static int xoptind = 0; /* Global argv index. */
|
||||
static int xgetopt(int argc, char *argv[], char *optstring)
|
||||
{
|
||||
static char *scan = NULL; /* Private scan pointer. */
|
||||
|
||||
char c;
|
||||
char *place;
|
||||
|
||||
xoptarg = NULL;
|
||||
|
||||
if (!scan || *scan == '\0') {
|
||||
if (xoptind == 0)
|
||||
xoptind++;
|
||||
|
||||
if (xoptind >= argc || argv[xoptind][0] != '-' || argv[xoptind][1] == '\0')
|
||||
return EOF;
|
||||
if (argv[xoptind][1] == '-' && argv[xoptind][2] == '\0') {
|
||||
xoptind++;
|
||||
return EOF;
|
||||
}
|
||||
|
||||
scan = argv[xoptind]+1;
|
||||
xoptind++;
|
||||
}
|
||||
|
||||
c = *scan++;
|
||||
place = strchr(optstring, c);
|
||||
|
||||
if (!place || c == ':') {
|
||||
fprintf(stderr, "%s: unknown option -%c\n", argv[0], c);
|
||||
return '?';
|
||||
}
|
||||
|
||||
place++;
|
||||
if (*place == ':') {
|
||||
if (*scan != '\0') {
|
||||
xoptarg = scan;
|
||||
scan = NULL;
|
||||
} else if (xoptind < argc) {
|
||||
xoptarg = argv[xoptind];
|
||||
xoptind++;
|
||||
} else {
|
||||
fprintf(stderr, "%s: option requires argument -%c\n", argv[0], c);
|
||||
return ':';
|
||||
}
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
#ifdef HAVE_READLINE
|
||||
#include <readline/readline.h>
|
||||
#include <readline/history.h>
|
||||
#else
|
||||
void using_history(void) { }
|
||||
void add_history(const char *string) { }
|
||||
void rl_bind_key(int key, void (*fun)(void)) { }
|
||||
void rl_insert(void) { }
|
||||
char *readline(const char *prompt)
|
||||
{
|
||||
static char line[500], *p;
|
||||
int n;
|
||||
fputs(prompt, stdout);
|
||||
p = fgets(line, sizeof line, stdin);
|
||||
if (p) {
|
||||
n = strlen(line);
|
||||
if (n > 0 && line[n-1] == '\n')
|
||||
line[--n] = 0;
|
||||
p = malloc(n+1);
|
||||
memcpy(p, line, n+1);
|
||||
return p;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define PS1 "> "
|
||||
|
||||
static void jsB_gc(js_State *J)
|
||||
{
|
||||
int report = js_toboolean(J, 1);
|
||||
js_gc(J, report);
|
||||
js_pushundefined(J);
|
||||
}
|
||||
|
||||
static void jsB_load(js_State *J)
|
||||
{
|
||||
int i, n = js_gettop(J);
|
||||
for (i = 1; i < n; ++i) {
|
||||
js_loadfile(J, js_tostring(J, i));
|
||||
js_pushundefined(J);
|
||||
js_call(J, 0);
|
||||
js_pop(J, 1);
|
||||
}
|
||||
js_pushundefined(J);
|
||||
}
|
||||
|
||||
static void jsB_compile(js_State *J)
|
||||
{
|
||||
const char *source = js_tostring(J, 1);
|
||||
const char *filename = js_isdefined(J, 2) ? js_tostring(J, 2) : "[string]";
|
||||
js_loadstring(J, filename, source);
|
||||
}
|
||||
|
||||
static void jsB_alert(js_State *J)
|
||||
{
|
||||
long message = (long)(js_isundefined(J, 1) ? "" : js_tostring(J, 1));
|
||||
os_call_ext_str_2("PopUpOk", message, 0);
|
||||
js_pushundefined(J);
|
||||
}
|
||||
|
||||
static void jsB_confirm(js_State *J)
|
||||
{
|
||||
long message = (long)(js_isundefined(J, 1) ? "" : js_tostring(J, 1));
|
||||
js_pushboolean(J, os_call_ext_str_2("PopUpCancelOk", message, 0));
|
||||
}
|
||||
|
||||
static void jsB_prompt(js_State *J)
|
||||
{
|
||||
long message = (long)(js_isundefined(J, 1) ? "" : js_tostring(J, 1));
|
||||
js_pushstring(J, (char*)os_call_ext_str_1("PopUpGetStr", message));
|
||||
}
|
||||
|
||||
static void jsB_print(js_State *J)
|
||||
{
|
||||
int i, top = js_gettop(J);
|
||||
for (i = 1; i < top; ++i) {
|
||||
const char *s = js_tostring(J, i);
|
||||
if (i > 1) putchar(' ');
|
||||
fputs(s, stdout);
|
||||
}
|
||||
putchar('\n');
|
||||
js_pushundefined(J);
|
||||
}
|
||||
|
||||
static void jsB_write(js_State *J)
|
||||
{
|
||||
int i, top = js_gettop(J);
|
||||
for (i = 1; i < top; ++i) {
|
||||
const char *s = js_tostring(J, i);
|
||||
if (i > 1) putchar(' ');
|
||||
fputs(s, stdout);
|
||||
}
|
||||
js_pushundefined(J);
|
||||
}
|
||||
|
||||
static void jsB_read(js_State *J)
|
||||
{
|
||||
const char *filename = js_tostring(J, 1);
|
||||
FILE *f;
|
||||
char *s;
|
||||
int n, t;
|
||||
|
||||
f = fopen(filename, "rb");
|
||||
if (!f) {
|
||||
js_error(J, "cannot open file '%s': %s", filename, strerror(errno));
|
||||
}
|
||||
|
||||
if (fseek(f, 0, SEEK_END) < 0) {
|
||||
fclose(f);
|
||||
js_error(J, "cannot seek in file '%s': %s", filename, strerror(errno));
|
||||
}
|
||||
|
||||
n = ftell(f);
|
||||
if (n < 0) {
|
||||
fclose(f);
|
||||
js_error(J, "cannot tell in file '%s': %s", filename, strerror(errno));
|
||||
}
|
||||
|
||||
if (fseek(f, 0, SEEK_SET) < 0) {
|
||||
fclose(f);
|
||||
js_error(J, "cannot seek in file '%s': %s", filename, strerror(errno));
|
||||
}
|
||||
|
||||
s = malloc(n + 1);
|
||||
if (!s) {
|
||||
fclose(f);
|
||||
js_error(J, "out of memory");
|
||||
}
|
||||
|
||||
t = fread(s, 1, n, f);
|
||||
if (t != n) {
|
||||
free(s);
|
||||
fclose(f);
|
||||
js_error(J, "cannot read data from file '%s': %s", filename, strerror(errno));
|
||||
}
|
||||
s[n] = 0;
|
||||
|
||||
js_pushstring(J, s);
|
||||
free(s);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
static void jsB_readline(js_State *J)
|
||||
{
|
||||
char *line = readline("");
|
||||
if (!line) {
|
||||
js_pushnull(J);
|
||||
return;
|
||||
}
|
||||
js_pushstring(J, line);
|
||||
if (*line)
|
||||
add_history(line);
|
||||
free(line);
|
||||
}
|
||||
|
||||
static void jsB_quit(js_State *J)
|
||||
{
|
||||
exit(js_tonumber(J, 1));
|
||||
}
|
||||
|
||||
static void jsB_repr(js_State *J)
|
||||
{
|
||||
js_repr(J, 1);
|
||||
}
|
||||
|
||||
static const char *require_js =
|
||||
"function require(name) {\n"
|
||||
"var cache = require.cache;\n"
|
||||
"if (name in cache) return cache[name];\n"
|
||||
"var exports = {};\n"
|
||||
"cache[name] = exports;\n"
|
||||
"Function('exports', read(name+'.js'))(exports);\n"
|
||||
"return exports;\n"
|
||||
"}\n"
|
||||
"require.cache = Object.create(null);\n"
|
||||
;
|
||||
|
||||
|
||||
static const char *stacktrace_js =
|
||||
"Error.prototype.toString = function() {\n"
|
||||
"var s = this.name;\n"
|
||||
"if ('message' in this) s += ': ' + this.message;\n"
|
||||
"if ('stackTrace' in this) s += this.stackTrace;\n"
|
||||
"return s;\n"
|
||||
"};\n"
|
||||
;
|
||||
|
||||
static const char *console_js =
|
||||
"var console = { log: print, debug: print, warn: print, error: print };"
|
||||
;
|
||||
|
||||
static int eval_file(js_State *J, const char* filename)
|
||||
{
|
||||
char* source = (char*)os_call_ext_str_3("FileRead", (uint64_t)filename, 0, 0);
|
||||
int res = js_ploadstring(J, filename, source);
|
||||
free(source);
|
||||
if (res) {
|
||||
fprintf(stderr, "%s\n", js_trystring(J, -1, "Error"));
|
||||
js_pop(J, 1);
|
||||
return 1;
|
||||
}
|
||||
js_pushundefined(J);
|
||||
if (js_pcall(J, 0)) {
|
||||
fprintf(stderr, "%s\n", js_trystring(J, -1, "Error"));
|
||||
js_pop(J, 1);
|
||||
return 1;
|
||||
}
|
||||
if (js_isdefined(J, -1)) {
|
||||
printf("%s\n", js_tryrepr(J, -1, "can't convert to string"));
|
||||
}
|
||||
js_pop(J, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int eval_print(js_State *J, const char *source)
|
||||
{
|
||||
if (js_ploadstring(J, "[stdin]", source)) {
|
||||
fprintf(stderr, "%s\n", js_trystring(J, -1, "Error"));
|
||||
js_pop(J, 1);
|
||||
return 1;
|
||||
}
|
||||
js_pushundefined(J);
|
||||
if (js_pcall(J, 0)) {
|
||||
fprintf(stderr, "%s\n", js_trystring(J, -1, "Error"));
|
||||
js_pop(J, 1);
|
||||
return 1;
|
||||
}
|
||||
if (js_isdefined(J, -1)) {
|
||||
printf("%s\n", js_tryrepr(J, -1, "can't convert to string"));
|
||||
}
|
||||
js_pop(J, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
fprintf(stderr, "Usage: mujs [options] [script [scriptArgs*]]\n");
|
||||
fprintf(stderr, "\t-i: Enter interactive prompt after running code.\n");
|
||||
fprintf(stderr, "\t-s: Check strictness.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
char *input;
|
||||
js_State *J;
|
||||
int status = 0;
|
||||
int strict = 0;
|
||||
int interactive = 0;
|
||||
int c;
|
||||
|
||||
while ((c = xgetopt(argc, argv, "is")) != -1) {
|
||||
switch (c) {
|
||||
default: usage(); break;
|
||||
case 'i': interactive = 1; break;
|
||||
case 's': strict = 1; break;
|
||||
}
|
||||
}
|
||||
|
||||
J = js_newstate(NULL, NULL, strict ? JS_STRICT : 0);
|
||||
if (!J) {
|
||||
fprintf(stderr, "Could not initialize MuJS.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
js_newcfunction(J, jsB_gc, "gc", 0);
|
||||
js_setglobal(J, "gc");
|
||||
|
||||
js_newcfunction(J, jsB_load, "load", 1);
|
||||
js_setglobal(J, "load");
|
||||
|
||||
js_newcfunction(J, jsB_compile, "compile", 2);
|
||||
js_setglobal(J, "compile");
|
||||
|
||||
js_newcfunction(J, jsB_alert, "alert", 0);
|
||||
js_setglobal(J, "alert");
|
||||
|
||||
js_newcfunction(J, jsB_confirm, "confirm", 0);
|
||||
js_setglobal(J, "confirm");
|
||||
|
||||
js_newcfunction(J, jsB_print, "print", 0);
|
||||
js_setglobal(J, "print");
|
||||
|
||||
js_newcfunction(J, jsB_prompt, "prompt", 0);
|
||||
js_setglobal(J, "prompt");
|
||||
|
||||
js_newcfunction(J, jsB_write, "write", 0);
|
||||
js_setglobal(J, "write");
|
||||
|
||||
js_newcfunction(J, jsB_read, "read", 1);
|
||||
js_setglobal(J, "read");
|
||||
|
||||
js_newcfunction(J, jsB_readline, "readline", 0);
|
||||
js_setglobal(J, "readline");
|
||||
|
||||
js_newcfunction(J, jsB_repr, "repr", 0);
|
||||
js_setglobal(J, "repr");
|
||||
|
||||
js_newcfunction(J, jsB_quit, "quit", 1);
|
||||
js_setglobal(J, "quit");
|
||||
|
||||
js_dostring(J, require_js);
|
||||
js_dostring(J, stacktrace_js);
|
||||
js_dostring(J, console_js);
|
||||
|
||||
js_initlibtemple(J);
|
||||
|
||||
if (xoptind == argc) {
|
||||
interactive = 1;
|
||||
} else {
|
||||
status = eval_file(J, argv[1]);
|
||||
}
|
||||
|
||||
if (interactive) {
|
||||
printf("Welcome to MuJS %d.%d.%d.\n",
|
||||
JS_VERSION_MAJOR, JS_VERSION_MINOR, JS_VERSION_PATCH);
|
||||
input = readline(PS1);
|
||||
for (;;) {
|
||||
eval_print(J, input);
|
||||
free(input);
|
||||
input = readline(PS1);
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
js_gc(J, 0);
|
||||
js_freestate(J);
|
||||
|
||||
exit(status);
|
||||
}
|
254
src/mujs/mujs.h
Normal file
254
src/mujs/mujs.h
Normal file
|
@ -0,0 +1,254 @@
|
|||
#ifndef mujs_h
|
||||
#define mujs_h
|
||||
|
||||
#include <setjmp.h> /* required for setjmp in fz_try macro */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define JS_VERSION_MAJOR 1
|
||||
#define JS_VERSION_MINOR 3
|
||||
#define JS_VERSION_PATCH 5
|
||||
|
||||
#define JS_VERSION (JS_VERSION_MAJOR * 10000 + JS_VERSION_MINOR * 100 + JS_VERSION_PATCH)
|
||||
#define JS_CHECKVERSION(x,y,z) (JS_VERSION >= ((x) * 10000 + (y) * 100 + (z)))
|
||||
|
||||
/* noreturn is a GCC extension */
|
||||
#ifdef __GNUC__
|
||||
#define JS_NORETURN __attribute__((noreturn))
|
||||
#else
|
||||
#ifdef _MSC_VER
|
||||
#define JS_NORETURN __declspec(noreturn)
|
||||
#else
|
||||
#define JS_NORETURN
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* GCC can do type checking of printf strings */
|
||||
#ifdef __printflike
|
||||
#define JS_PRINTFLIKE __printflike
|
||||
#else
|
||||
#if __GNUC__ > 2 || __GNUC__ == 2 && __GNUC_MINOR__ >= 7
|
||||
#define JS_PRINTFLIKE(fmtarg, firstvararg) \
|
||||
__attribute__((__format__ (__printf__, fmtarg, firstvararg)))
|
||||
#else
|
||||
#define JS_PRINTFLIKE(fmtarg, firstvararg)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef struct js_State js_State;
|
||||
|
||||
typedef void *(*js_Alloc)(void *memctx, void *ptr, int size);
|
||||
typedef void (*js_Panic)(js_State *J);
|
||||
typedef void (*js_CFunction)(js_State *J);
|
||||
typedef void (*js_Finalize)(js_State *J, void *p);
|
||||
typedef int (*js_HasProperty)(js_State *J, void *p, const char *name);
|
||||
typedef int (*js_Put)(js_State *J, void *p, const char *name);
|
||||
typedef int (*js_Delete)(js_State *J, void *p, const char *name);
|
||||
typedef void (*js_Report)(js_State *J, const char *message);
|
||||
|
||||
/* Basic functions */
|
||||
js_State *js_newstate(js_Alloc alloc, void *actx, int flags);
|
||||
void js_setcontext(js_State *J, void *uctx);
|
||||
void *js_getcontext(js_State *J);
|
||||
void js_setreport(js_State *J, js_Report report);
|
||||
js_Panic js_atpanic(js_State *J, js_Panic panic);
|
||||
void js_freestate(js_State *J);
|
||||
void js_gc(js_State *J, int report);
|
||||
|
||||
int js_dostring(js_State *J, const char *source);
|
||||
int js_dofile(js_State *J, const char *filename);
|
||||
int js_ploadstring(js_State *J, const char *filename, const char *source);
|
||||
int js_ploadfile(js_State *J, const char *filename);
|
||||
int js_pcall(js_State *J, int n);
|
||||
int js_pconstruct(js_State *J, int n);
|
||||
|
||||
/* Exception handling */
|
||||
|
||||
void *js_savetry(js_State *J); /* returns a jmp_buf */
|
||||
|
||||
#define js_try(J) \
|
||||
setjmp(js_savetry(J))
|
||||
|
||||
void js_endtry(js_State *J);
|
||||
|
||||
/* State constructor flags */
|
||||
enum {
|
||||
JS_STRICT = 1,
|
||||
};
|
||||
|
||||
/* RegExp flags */
|
||||
enum {
|
||||
JS_REGEXP_G = 1,
|
||||
JS_REGEXP_I = 2,
|
||||
JS_REGEXP_M = 4,
|
||||
};
|
||||
|
||||
/* Property attribute flags */
|
||||
enum {
|
||||
JS_READONLY = 1,
|
||||
JS_DONTENUM = 2,
|
||||
JS_DONTCONF = 4,
|
||||
};
|
||||
|
||||
/* enum for js_type() */
|
||||
enum {
|
||||
JS_ISUNDEFINED,
|
||||
JS_ISNULL,
|
||||
JS_ISBOOLEAN,
|
||||
JS_ISNUMBER,
|
||||
JS_ISSTRING,
|
||||
JS_ISFUNCTION,
|
||||
JS_ISOBJECT
|
||||
};
|
||||
|
||||
void js_report(js_State *J, const char *message);
|
||||
|
||||
void js_newerror(js_State *J, const char *message);
|
||||
void js_newevalerror(js_State *J, const char *message);
|
||||
void js_newrangeerror(js_State *J, const char *message);
|
||||
void js_newreferenceerror(js_State *J, const char *message);
|
||||
void js_newsyntaxerror(js_State *J, const char *message);
|
||||
void js_newtypeerror(js_State *J, const char *message);
|
||||
void js_newurierror(js_State *J, const char *message);
|
||||
|
||||
JS_NORETURN void js_error(js_State *J, const char *fmt, ...) JS_PRINTFLIKE(2,3);
|
||||
JS_NORETURN void js_evalerror(js_State *J, const char *fmt, ...) JS_PRINTFLIKE(2,3);
|
||||
JS_NORETURN void js_rangeerror(js_State *J, const char *fmt, ...) JS_PRINTFLIKE(2,3);
|
||||
JS_NORETURN void js_referenceerror(js_State *J, const char *fmt, ...) JS_PRINTFLIKE(2,3);
|
||||
JS_NORETURN void js_syntaxerror(js_State *J, const char *fmt, ...) JS_PRINTFLIKE(2,3);
|
||||
JS_NORETURN void js_typeerror(js_State *J, const char *fmt, ...) JS_PRINTFLIKE(2,3);
|
||||
JS_NORETURN void js_urierror(js_State *J, const char *fmt, ...) JS_PRINTFLIKE(2,3);
|
||||
JS_NORETURN void js_throw(js_State *J);
|
||||
|
||||
void js_loadstring(js_State *J, const char *filename, const char *source);
|
||||
void js_loadfile(js_State *J, const char *filename);
|
||||
|
||||
void js_eval(js_State *J);
|
||||
void js_call(js_State *J, int n);
|
||||
void js_construct(js_State *J, int n);
|
||||
|
||||
const char *js_ref(js_State *J);
|
||||
void js_unref(js_State *J, const char *ref);
|
||||
|
||||
void js_getregistry(js_State *J, const char *name);
|
||||
void js_setregistry(js_State *J, const char *name);
|
||||
void js_delregistry(js_State *J, const char *name);
|
||||
|
||||
void js_getglobal(js_State *J, const char *name);
|
||||
void js_setglobal(js_State *J, const char *name);
|
||||
void js_defglobal(js_State *J, const char *name, int atts);
|
||||
void js_delglobal(js_State *J, const char *name);
|
||||
|
||||
int js_hasproperty(js_State *J, int idx, const char *name);
|
||||
void js_getproperty(js_State *J, int idx, const char *name);
|
||||
void js_setproperty(js_State *J, int idx, const char *name);
|
||||
void js_defproperty(js_State *J, int idx, const char *name, int atts);
|
||||
void js_delproperty(js_State *J, int idx, const char *name);
|
||||
void js_defaccessor(js_State *J, int idx, const char *name, int atts);
|
||||
|
||||
int js_getlength(js_State *J, int idx);
|
||||
void js_setlength(js_State *J, int idx, int len);
|
||||
int js_hasindex(js_State *J, int idx, int i);
|
||||
void js_getindex(js_State *J, int idx, int i);
|
||||
void js_setindex(js_State *J, int idx, int i);
|
||||
void js_delindex(js_State *J, int idx, int i);
|
||||
|
||||
void js_currentfunction(js_State *J);
|
||||
void *js_currentfunctiondata(js_State *J);
|
||||
void js_pushglobal(js_State *J);
|
||||
void js_pushundefined(js_State *J);
|
||||
void js_pushnull(js_State *J);
|
||||
void js_pushboolean(js_State *J, int v);
|
||||
void js_pushnumber(js_State *J, double v);
|
||||
void js_pushstring(js_State *J, const char *v);
|
||||
void js_pushlstring(js_State *J, const char *v, int n);
|
||||
void js_pushliteral(js_State *J, const char *v);
|
||||
|
||||
void js_newobjectx(js_State *J);
|
||||
void js_newobject(js_State *J);
|
||||
void js_newarray(js_State *J);
|
||||
void js_newboolean(js_State *J, int v);
|
||||
void js_newnumber(js_State *J, double v);
|
||||
void js_newstring(js_State *J, const char *v);
|
||||
void js_newcfunction(js_State *J, js_CFunction fun, const char *name, int length);
|
||||
void js_newcfunctionx(js_State *J, js_CFunction fun, const char *name, int length, void *data, js_Finalize finalize);
|
||||
void js_newcconstructor(js_State *J, js_CFunction fun, js_CFunction con, const char *name, int length);
|
||||
void js_newuserdata(js_State *J, const char *tag, void *data, js_Finalize finalize);
|
||||
void js_newuserdatax(js_State *J, const char *tag, void *data, js_HasProperty has, js_Put put, js_Delete del, js_Finalize finalize);
|
||||
void js_newregexp(js_State *J, const char *pattern, int flags);
|
||||
|
||||
void js_pushiterator(js_State *J, int idx, int own);
|
||||
const char *js_nextiterator(js_State *J, int idx);
|
||||
|
||||
int js_isdefined(js_State *J, int idx);
|
||||
int js_isundefined(js_State *J, int idx);
|
||||
int js_isnull(js_State *J, int idx);
|
||||
int js_isboolean(js_State *J, int idx);
|
||||
int js_isnumber(js_State *J, int idx);
|
||||
int js_isstring(js_State *J, int idx);
|
||||
int js_isprimitive(js_State *J, int idx);
|
||||
int js_isobject(js_State *J, int idx);
|
||||
int js_isarray(js_State *J, int idx);
|
||||
int js_isregexp(js_State *J, int idx);
|
||||
int js_iscoercible(js_State *J, int idx);
|
||||
int js_iscallable(js_State *J, int idx);
|
||||
int js_isuserdata(js_State *J, int idx, const char *tag);
|
||||
int js_iserror(js_State *J, int idx);
|
||||
int js_isnumberobject(js_State *J, int idx);
|
||||
int js_isstringobject(js_State *J, int idx);
|
||||
int js_isbooleanobject(js_State *J, int idx);
|
||||
int js_isdateobject(js_State *J, int idx);
|
||||
|
||||
int js_toboolean(js_State *J, int idx);
|
||||
double js_tonumber(js_State *J, int idx);
|
||||
const char *js_tostring(js_State *J, int idx);
|
||||
void *js_touserdata(js_State *J, int idx, const char *tag);
|
||||
|
||||
const char *js_trystring(js_State *J, int idx, const char *error);
|
||||
double js_trynumber(js_State *J, int idx, double error);
|
||||
int js_tryinteger(js_State *J, int idx, int error);
|
||||
int js_tryboolean(js_State *J, int idx, int error);
|
||||
|
||||
int js_tointeger(js_State *J, int idx);
|
||||
int js_toint32(js_State *J, int idx);
|
||||
unsigned int js_touint32(js_State *J, int idx);
|
||||
short js_toint16(js_State *J, int idx);
|
||||
unsigned short js_touint16(js_State *J, int idx);
|
||||
|
||||
int js_gettop(js_State *J);
|
||||
void js_pop(js_State *J, int n);
|
||||
void js_rot(js_State *J, int n);
|
||||
void js_copy(js_State *J, int idx);
|
||||
void js_remove(js_State *J, int idx);
|
||||
void js_insert(js_State *J, int idx);
|
||||
void js_replace(js_State* J, int idx);
|
||||
|
||||
void js_dup(js_State *J);
|
||||
void js_dup2(js_State *J);
|
||||
void js_rot2(js_State *J);
|
||||
void js_rot3(js_State *J);
|
||||
void js_rot4(js_State *J);
|
||||
void js_rot2pop1(js_State *J);
|
||||
void js_rot3pop2(js_State *J);
|
||||
|
||||
void js_concat(js_State *J);
|
||||
int js_compare(js_State *J, int *okay);
|
||||
int js_equal(js_State *J);
|
||||
int js_strictequal(js_State *J);
|
||||
int js_instanceof(js_State *J);
|
||||
const char *js_typeof(js_State *J, int idx);
|
||||
int js_type(js_State *J, int idx);
|
||||
|
||||
void js_repr(js_State *J, int idx);
|
||||
const char *js_torepr(js_State *J, int idx);
|
||||
const char *js_tryrepr(js_State *J, int idx, const char *error);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
void js_initlibtemple(js_State *J);
|
1203
src/mujs/nanoprintf.h
Normal file
1203
src/mujs/nanoprintf.h
Normal file
File diff suppressed because it is too large
Load diff
85
src/mujs/opnames.h
Normal file
85
src/mujs/opnames.h
Normal file
|
@ -0,0 +1,85 @@
|
|||
"pop",
|
||||
"dup",
|
||||
"dup2",
|
||||
"rot2",
|
||||
"rot3",
|
||||
"rot4",
|
||||
"integer",
|
||||
"number",
|
||||
"string",
|
||||
"closure",
|
||||
"newarray",
|
||||
"newobject",
|
||||
"newregexp",
|
||||
"undef",
|
||||
"null",
|
||||
"true",
|
||||
"false",
|
||||
"this",
|
||||
"current",
|
||||
"getlocal",
|
||||
"setlocal",
|
||||
"dellocal",
|
||||
"hasvar",
|
||||
"getvar",
|
||||
"setvar",
|
||||
"delvar",
|
||||
"in",
|
||||
"skiparray",
|
||||
"initarray",
|
||||
"initprop",
|
||||
"initgetter",
|
||||
"initsetter",
|
||||
"getprop",
|
||||
"getprop_s",
|
||||
"setprop",
|
||||
"setprop_s",
|
||||
"delprop",
|
||||
"delprop_s",
|
||||
"iterator",
|
||||
"nextiter",
|
||||
"eval",
|
||||
"call",
|
||||
"new",
|
||||
"typeof",
|
||||
"pos",
|
||||
"neg",
|
||||
"bitnot",
|
||||
"lognot",
|
||||
"inc",
|
||||
"dec",
|
||||
"postinc",
|
||||
"postdec",
|
||||
"mul",
|
||||
"div",
|
||||
"mod",
|
||||
"add",
|
||||
"sub",
|
||||
"shl",
|
||||
"shr",
|
||||
"ushr",
|
||||
"lt",
|
||||
"gt",
|
||||
"le",
|
||||
"ge",
|
||||
"eq",
|
||||
"ne",
|
||||
"stricteq",
|
||||
"strictne",
|
||||
"jcase",
|
||||
"bitand",
|
||||
"bitxor",
|
||||
"bitor",
|
||||
"instanceof",
|
||||
"throw",
|
||||
"try",
|
||||
"endtry",
|
||||
"catch",
|
||||
"endcatch",
|
||||
"with",
|
||||
"endwith",
|
||||
"debugger",
|
||||
"jump",
|
||||
"jtrue",
|
||||
"jfalse",
|
||||
"return",
|
980
src/mujs/pp.c
Normal file
980
src/mujs/pp.c
Normal file
|
@ -0,0 +1,980 @@
|
|||
/* Pretty-print input source by emitting parse tree back as syntax.
|
||||
* with no flags: pretty-printed source
|
||||
* with -m: minified source with line breaks
|
||||
* with -mm: minified source without line breaks
|
||||
* with -s: s-expression syntax tree
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "jsi.h"
|
||||
#include "utf.h"
|
||||
|
||||
static const char *astname[] = {
|
||||
#include "astnames.h"
|
||||
NULL
|
||||
};
|
||||
|
||||
static const char *opname[] = {
|
||||
#include "opnames.h"
|
||||
NULL
|
||||
};
|
||||
|
||||
static int format = 0;
|
||||
static int minify = 0;
|
||||
|
||||
static void pc(int c)
|
||||
{
|
||||
putchar(c);
|
||||
}
|
||||
|
||||
static void ps(const char *s)
|
||||
{
|
||||
fputs(s, stdout);
|
||||
}
|
||||
|
||||
static void in(int d)
|
||||
{
|
||||
if (minify < 1)
|
||||
while (d-- > 0)
|
||||
putchar('\t');
|
||||
}
|
||||
|
||||
static void nl(void)
|
||||
{
|
||||
if (minify < 2)
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
static void sp(void)
|
||||
{
|
||||
if (minify < 1)
|
||||
putchar(' ');
|
||||
}
|
||||
|
||||
static void comma(void)
|
||||
{
|
||||
putchar(',');
|
||||
sp();
|
||||
}
|
||||
|
||||
static void pstr(const char *s)
|
||||
{
|
||||
static const char *HEX = "0123456789ABCDEF";
|
||||
Rune c;
|
||||
pc(minify ? '\'' : '"');
|
||||
while (*s) {
|
||||
s += chartorune(&c, s);
|
||||
switch (c) {
|
||||
case '\'': ps("\\'"); break;
|
||||
case '"': ps("\\\""); break;
|
||||
case '\\': ps("\\\\"); break;
|
||||
case '\b': ps("\\b"); break;
|
||||
case '\f': ps("\\f"); break;
|
||||
case '\n': ps("\\n"); break;
|
||||
case '\r': ps("\\r"); break;
|
||||
case '\t': ps("\\t"); break;
|
||||
default:
|
||||
if (c < ' ' || c > 127) {
|
||||
ps("\\u");
|
||||
pc(HEX[(c>>12)&15]);
|
||||
pc(HEX[(c>>8)&15]);
|
||||
pc(HEX[(c>>4)&15]);
|
||||
pc(HEX[c&15]);
|
||||
} else {
|
||||
pc(c); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
pc(minify ? '\'' : '"');
|
||||
}
|
||||
|
||||
static void pregexp(const char *prog, int flags)
|
||||
{
|
||||
pc('/');
|
||||
while (*prog) {
|
||||
if (*prog == '/')
|
||||
pc('\\');
|
||||
pc(*prog);
|
||||
++prog;
|
||||
}
|
||||
pc('/');
|
||||
if (flags & JS_REGEXP_G) pc('g');
|
||||
if (flags & JS_REGEXP_I) pc('i');
|
||||
if (flags & JS_REGEXP_M) pc('m');
|
||||
}
|
||||
|
||||
/* Bytecode */
|
||||
|
||||
static void jsC_dumpfunction(js_State *J, js_Function *F)
|
||||
{
|
||||
js_Instruction *p = F->code;
|
||||
js_Instruction *end = F->code + F->codelen;
|
||||
char *s;
|
||||
double n;
|
||||
int i;
|
||||
|
||||
printf("%s(%d)\n", F->name, F->numparams);
|
||||
if (F->strict) printf("\tstrict\n");
|
||||
if (F->lightweight) printf("\tlightweight\n");
|
||||
if (F->arguments) printf("\targuments\n");
|
||||
printf("\tsource %s:%d\n", F->filename, F->line);
|
||||
for (i = 0; i < F->funlen; ++i)
|
||||
printf("\tfunction %d %s\n", i, F->funtab[i]->name);
|
||||
for (i = 0; i < F->varlen; ++i)
|
||||
printf("\tlocal %d %s\n", i + 1, F->vartab[i]);
|
||||
|
||||
printf("{\n");
|
||||
while (p < end) {
|
||||
int ln = *p++;
|
||||
int c = *p++;
|
||||
|
||||
printf("%5d(%3d): ", (int)(p - F->code) - 2, ln);
|
||||
ps(opname[c]);
|
||||
|
||||
switch (c) {
|
||||
case OP_INTEGER:
|
||||
printf(" %ld", (long)((*p++) - 32768));
|
||||
break;
|
||||
case OP_NUMBER:
|
||||
memcpy(&n, p, sizeof(n));
|
||||
p += sizeof(n) / sizeof(*p);
|
||||
printf(" %.9g", n);
|
||||
break;
|
||||
case OP_STRING:
|
||||
memcpy(&s, p, sizeof(s));
|
||||
p += sizeof(s) / sizeof(*p);
|
||||
pc(' ');
|
||||
pstr(s);
|
||||
break;
|
||||
case OP_NEWREGEXP:
|
||||
pc(' ');
|
||||
memcpy(&s, p, sizeof(s));
|
||||
p += sizeof(s) / sizeof(*p);
|
||||
pregexp(s, *p++);
|
||||
break;
|
||||
|
||||
case OP_GETVAR:
|
||||
case OP_HASVAR:
|
||||
case OP_SETVAR:
|
||||
case OP_DELVAR:
|
||||
case OP_GETPROP_S:
|
||||
case OP_SETPROP_S:
|
||||
case OP_DELPROP_S:
|
||||
case OP_CATCH:
|
||||
memcpy(&s, p, sizeof(s));
|
||||
p += sizeof(s) / sizeof(*p);
|
||||
pc(' ');
|
||||
ps(s);
|
||||
break;
|
||||
|
||||
case OP_GETLOCAL:
|
||||
case OP_SETLOCAL:
|
||||
case OP_DELLOCAL:
|
||||
printf(" %s", F->vartab[*p++ - 1]);
|
||||
break;
|
||||
|
||||
case OP_CLOSURE:
|
||||
case OP_CALL:
|
||||
case OP_NEW:
|
||||
case OP_JUMP:
|
||||
case OP_JTRUE:
|
||||
case OP_JFALSE:
|
||||
case OP_JCASE:
|
||||
case OP_TRY:
|
||||
printf(" %ld", (long)*p++);
|
||||
break;
|
||||
}
|
||||
|
||||
putchar('\n');
|
||||
}
|
||||
printf("}\n");
|
||||
|
||||
for (i = 0; i < F->funlen; ++i) {
|
||||
if (F->funtab[i] != F) {
|
||||
printf("function %d ", i);
|
||||
jsC_dumpfunction(J, F->funtab[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Pretty-printed Javascript syntax */
|
||||
|
||||
static int prec(enum js_AstType type)
|
||||
{
|
||||
switch (type) {
|
||||
case AST_IDENTIFIER:
|
||||
case EXP_IDENTIFIER:
|
||||
case EXP_NUMBER:
|
||||
case EXP_STRING:
|
||||
case EXP_REGEXP:
|
||||
case EXP_ELISION:
|
||||
case EXP_NULL:
|
||||
case EXP_TRUE:
|
||||
case EXP_FALSE:
|
||||
case EXP_THIS:
|
||||
case EXP_ARRAY:
|
||||
case EXP_OBJECT:
|
||||
return 170;
|
||||
|
||||
case EXP_FUN:
|
||||
case EXP_INDEX:
|
||||
case EXP_MEMBER:
|
||||
case EXP_CALL:
|
||||
case EXP_NEW:
|
||||
return 160;
|
||||
|
||||
case EXP_POSTINC:
|
||||
case EXP_POSTDEC:
|
||||
return 150;
|
||||
|
||||
case EXP_DELETE:
|
||||
case EXP_VOID:
|
||||
case EXP_TYPEOF:
|
||||
case EXP_PREINC:
|
||||
case EXP_PREDEC:
|
||||
case EXP_POS:
|
||||
case EXP_NEG:
|
||||
case EXP_BITNOT:
|
||||
case EXP_LOGNOT:
|
||||
return 140;
|
||||
|
||||
case EXP_MOD:
|
||||
case EXP_DIV:
|
||||
case EXP_MUL:
|
||||
return 130;
|
||||
|
||||
case EXP_SUB:
|
||||
case EXP_ADD:
|
||||
return 120;
|
||||
|
||||
case EXP_USHR:
|
||||
case EXP_SHR:
|
||||
case EXP_SHL:
|
||||
return 110;
|
||||
|
||||
case EXP_IN:
|
||||
case EXP_INSTANCEOF:
|
||||
case EXP_GE:
|
||||
case EXP_LE:
|
||||
case EXP_GT:
|
||||
case EXP_LT:
|
||||
return 100;
|
||||
|
||||
case EXP_STRICTNE:
|
||||
case EXP_STRICTEQ:
|
||||
case EXP_NE:
|
||||
case EXP_EQ:
|
||||
return 90;
|
||||
|
||||
case EXP_BITAND: return 80;
|
||||
case EXP_BITXOR: return 70;
|
||||
case EXP_BITOR: return 60;
|
||||
case EXP_LOGAND: return 50;
|
||||
case EXP_LOGOR: return 40;
|
||||
|
||||
case EXP_COND:
|
||||
return 30;
|
||||
|
||||
case EXP_ASS:
|
||||
case EXP_ASS_MUL:
|
||||
case EXP_ASS_DIV:
|
||||
case EXP_ASS_MOD:
|
||||
case EXP_ASS_ADD:
|
||||
case EXP_ASS_SUB:
|
||||
case EXP_ASS_SHL:
|
||||
case EXP_ASS_SHR:
|
||||
case EXP_ASS_USHR:
|
||||
case EXP_ASS_BITAND:
|
||||
case EXP_ASS_BITXOR:
|
||||
case EXP_ASS_BITOR:
|
||||
return 20;
|
||||
|
||||
#define COMMA 15
|
||||
|
||||
case EXP_COMMA:
|
||||
return 10;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void pstmlist(int d, js_Ast *list);
|
||||
static void pexpi(int d, int i, js_Ast *exp);
|
||||
static void pstm(int d, js_Ast *stm);
|
||||
static void slist(int d, js_Ast *list);
|
||||
static void sblock(int d, js_Ast *list);
|
||||
|
||||
static void pargs(int d, js_Ast *list)
|
||||
{
|
||||
while (list) {
|
||||
assert(list->type == AST_LIST);
|
||||
pexpi(d, COMMA, list->a);
|
||||
list = list->b;
|
||||
if (list)
|
||||
comma();
|
||||
}
|
||||
}
|
||||
|
||||
static void parray(int d, js_Ast *list)
|
||||
{
|
||||
pc('[');
|
||||
while (list) {
|
||||
assert(list->type == AST_LIST);
|
||||
pexpi(d, COMMA, list->a);
|
||||
list = list->b;
|
||||
if (list)
|
||||
comma();
|
||||
}
|
||||
pc(']');
|
||||
}
|
||||
|
||||
static void pobject(int d, js_Ast *list)
|
||||
{
|
||||
pc('{');
|
||||
if (list) {
|
||||
nl();
|
||||
in(d+1);
|
||||
}
|
||||
while (list) {
|
||||
js_Ast *kv = list->a;
|
||||
assert(list->type == AST_LIST);
|
||||
switch (kv->type) {
|
||||
default: break;
|
||||
case EXP_PROP_VAL:
|
||||
pexpi(d+1, COMMA, kv->a);
|
||||
pc(':'); sp();
|
||||
pexpi(d+1, COMMA, kv->b);
|
||||
break;
|
||||
case EXP_PROP_GET:
|
||||
ps("get ");
|
||||
pexpi(d+1, COMMA, kv->a);
|
||||
ps("()"); sp(); pc('{'); nl();
|
||||
pstmlist(d+1, kv->c);
|
||||
in(d+1); pc('}');
|
||||
break;
|
||||
case EXP_PROP_SET:
|
||||
ps("set ");
|
||||
pexpi(d+1, COMMA, kv->a);
|
||||
pc('(');
|
||||
pargs(d+1, kv->b);
|
||||
pc(')'); sp(); pc('{'); nl();
|
||||
pstmlist(d+1, kv->c);
|
||||
in(d+1); pc('}');
|
||||
break;
|
||||
}
|
||||
list = list->b;
|
||||
if (list) {
|
||||
pc(',');
|
||||
nl();
|
||||
in(d+1);
|
||||
} else {
|
||||
nl();
|
||||
in(d);
|
||||
}
|
||||
}
|
||||
pc('}');
|
||||
}
|
||||
|
||||
static void pbin(int d, int p, js_Ast *exp, const char *op)
|
||||
{
|
||||
pexpi(d, p, exp->a);
|
||||
sp();
|
||||
ps(op);
|
||||
sp();
|
||||
pexpi(d, p, exp->b);
|
||||
}
|
||||
|
||||
static void puna(int d, int p, js_Ast *exp, const char *pre, const char *suf)
|
||||
{
|
||||
ps(pre);
|
||||
pexpi(d, p, exp->a);
|
||||
ps(suf);
|
||||
}
|
||||
|
||||
static void pexpi(int d, int p, js_Ast *exp)
|
||||
{
|
||||
int tp, paren;
|
||||
|
||||
if (!exp) return;
|
||||
|
||||
tp = prec(exp->type);
|
||||
paren = 0;
|
||||
if (tp < p) {
|
||||
pc('(');
|
||||
paren = 1;
|
||||
}
|
||||
p = tp;
|
||||
|
||||
switch (exp->type) {
|
||||
case AST_IDENTIFIER: ps(exp->string); break;
|
||||
case EXP_IDENTIFIER: ps(exp->string); break;
|
||||
case EXP_NUMBER: printf("%.9g", exp->number); break;
|
||||
case EXP_STRING: pstr(exp->string); break;
|
||||
case EXP_REGEXP: pregexp(exp->string, exp->number); break;
|
||||
|
||||
case EXP_ELISION: ps("elision"); break;
|
||||
case EXP_NULL: ps("null"); break;
|
||||
case EXP_TRUE: ps("true"); break;
|
||||
case EXP_FALSE: ps("false"); break;
|
||||
case EXP_THIS: ps("this"); break;
|
||||
|
||||
case EXP_OBJECT: pobject(d, exp->a); break;
|
||||
case EXP_ARRAY: parray(d, exp->a); break;
|
||||
|
||||
case EXP_DELETE: puna(d, p, exp, "delete ", ""); break;
|
||||
case EXP_VOID: puna(d, p, exp, "void ", ""); break;
|
||||
case EXP_TYPEOF: puna(d, p, exp, "typeof ", ""); break;
|
||||
case EXP_PREINC: puna(d, p, exp, "++", ""); break;
|
||||
case EXP_PREDEC: puna(d, p, exp, "--", ""); break;
|
||||
case EXP_POSTINC: puna(d, p, exp, "", "++"); break;
|
||||
case EXP_POSTDEC: puna(d, p, exp, "", "--"); break;
|
||||
case EXP_POS: puna(d, p, exp, "+", ""); break;
|
||||
case EXP_NEG: puna(d, p, exp, "-", ""); break;
|
||||
case EXP_BITNOT: puna(d, p, exp, "~", ""); break;
|
||||
case EXP_LOGNOT: puna(d, p, exp, "!", ""); break;
|
||||
|
||||
case EXP_LOGOR: pbin(d, p, exp, "||"); break;
|
||||
case EXP_LOGAND: pbin(d, p, exp, "&&"); break;
|
||||
case EXP_BITOR: pbin(d, p, exp, "|"); break;
|
||||
case EXP_BITXOR: pbin(d, p, exp, "^"); break;
|
||||
case EXP_BITAND: pbin(d, p, exp, "&"); break;
|
||||
case EXP_EQ: pbin(d, p, exp, "=="); break;
|
||||
case EXP_NE: pbin(d, p, exp, "!="); break;
|
||||
case EXP_STRICTEQ: pbin(d, p, exp, "==="); break;
|
||||
case EXP_STRICTNE: pbin(d, p, exp, "!=="); break;
|
||||
case EXP_LT: pbin(d, p, exp, "<"); break;
|
||||
case EXP_GT: pbin(d, p, exp, ">"); break;
|
||||
case EXP_LE: pbin(d, p, exp, "<="); break;
|
||||
case EXP_GE: pbin(d, p, exp, ">="); break;
|
||||
case EXP_IN: pbin(d, p, exp, "in"); break;
|
||||
case EXP_SHL: pbin(d, p, exp, "<<"); break;
|
||||
case EXP_SHR: pbin(d, p, exp, ">>"); break;
|
||||
case EXP_USHR: pbin(d, p, exp, ">>>"); break;
|
||||
case EXP_ADD: pbin(d, p, exp, "+"); break;
|
||||
case EXP_SUB: pbin(d, p, exp, "-"); break;
|
||||
case EXP_MUL: pbin(d, p, exp, "*"); break;
|
||||
case EXP_DIV: pbin(d, p, exp, "/"); break;
|
||||
case EXP_MOD: pbin(d, p, exp, "%"); break;
|
||||
case EXP_ASS: pbin(d, p, exp, "="); break;
|
||||
case EXP_ASS_MUL: pbin(d, p, exp, "*="); break;
|
||||
case EXP_ASS_DIV: pbin(d, p, exp, "/="); break;
|
||||
case EXP_ASS_MOD: pbin(d, p, exp, "%="); break;
|
||||
case EXP_ASS_ADD: pbin(d, p, exp, "+="); break;
|
||||
case EXP_ASS_SUB: pbin(d, p, exp, "-="); break;
|
||||
case EXP_ASS_SHL: pbin(d, p, exp, "<<="); break;
|
||||
case EXP_ASS_SHR: pbin(d, p, exp, ">>="); break;
|
||||
case EXP_ASS_USHR: pbin(d, p, exp, ">>>="); break;
|
||||
case EXP_ASS_BITAND: pbin(d, p, exp, "&="); break;
|
||||
case EXP_ASS_BITXOR: pbin(d, p, exp, "^="); break;
|
||||
case EXP_ASS_BITOR: pbin(d, p, exp, "|="); break;
|
||||
|
||||
case EXP_INSTANCEOF:
|
||||
pexpi(d, p, exp->a);
|
||||
ps(" instanceof ");
|
||||
pexpi(d, p, exp->b);
|
||||
break;
|
||||
|
||||
case EXP_COMMA:
|
||||
pexpi(d, p, exp->a);
|
||||
pc(','); sp();
|
||||
pexpi(d, p, exp->b);
|
||||
break;
|
||||
|
||||
case EXP_COND:
|
||||
pexpi(d, p, exp->a);
|
||||
sp(); pc('?'); sp();
|
||||
pexpi(d, p, exp->b);
|
||||
sp(); pc(':'); sp();
|
||||
pexpi(d, p, exp->c);
|
||||
break;
|
||||
|
||||
case EXP_INDEX:
|
||||
pexpi(d, p, exp->a);
|
||||
pc('[');
|
||||
pexpi(d, 0, exp->b);
|
||||
pc(']');
|
||||
break;
|
||||
|
||||
case EXP_MEMBER:
|
||||
pexpi(d, p, exp->a);
|
||||
pc('.');
|
||||
pexpi(d, 0, exp->b);
|
||||
break;
|
||||
|
||||
case EXP_CALL:
|
||||
pexpi(d, p, exp->a);
|
||||
pc('(');
|
||||
pargs(d, exp->b);
|
||||
pc(')');
|
||||
break;
|
||||
|
||||
case EXP_NEW:
|
||||
ps("new ");
|
||||
pexpi(d, p, exp->a);
|
||||
pc('(');
|
||||
pargs(d, exp->b);
|
||||
pc(')');
|
||||
break;
|
||||
|
||||
case EXP_FUN:
|
||||
if (p == 0) pc('(');
|
||||
ps("function ");
|
||||
pexpi(d, 0, exp->a);
|
||||
pc('(');
|
||||
pargs(d, exp->b);
|
||||
pc(')'); sp(); pc('{'); nl();
|
||||
pstmlist(d, exp->c);
|
||||
in(d); pc('}');
|
||||
if (p == 0) pc(')');
|
||||
break;
|
||||
|
||||
default:
|
||||
ps("<UNKNOWN>");
|
||||
break;
|
||||
}
|
||||
|
||||
if (paren) pc(')');
|
||||
}
|
||||
|
||||
static void pexp(int d, js_Ast *exp)
|
||||
{
|
||||
pexpi(d, 0, exp);
|
||||
}
|
||||
|
||||
static void pvar(int d, js_Ast *var)
|
||||
{
|
||||
assert(var->type == EXP_VAR);
|
||||
pexp(d, var->a);
|
||||
if (var->b) {
|
||||
sp(); pc('='); sp();
|
||||
pexp(d, var->b);
|
||||
}
|
||||
}
|
||||
|
||||
static void pvarlist(int d, js_Ast *list)
|
||||
{
|
||||
while (list) {
|
||||
assert(list->type == AST_LIST);
|
||||
pvar(d, list->a);
|
||||
list = list->b;
|
||||
if (list)
|
||||
comma();
|
||||
}
|
||||
}
|
||||
|
||||
static void pblock(int d, js_Ast *block)
|
||||
{
|
||||
assert(block->type == STM_BLOCK);
|
||||
pc('{'); nl();
|
||||
pstmlist(d, block->a);
|
||||
in(d); pc('}');
|
||||
}
|
||||
|
||||
static void pstmh(int d, js_Ast *stm)
|
||||
{
|
||||
if (stm->type == STM_BLOCK) {
|
||||
sp();
|
||||
pblock(d, stm);
|
||||
} else {
|
||||
nl();
|
||||
pstm(d+1, stm);
|
||||
}
|
||||
}
|
||||
|
||||
static void pcaselist(int d, js_Ast *list)
|
||||
{
|
||||
while (list) {
|
||||
js_Ast *stm = list->a;
|
||||
if (stm->type == STM_CASE) {
|
||||
in(d); ps("case "); pexp(d, stm->a); pc(':'); nl();
|
||||
pstmlist(d, stm->b);
|
||||
}
|
||||
if (stm->type == STM_DEFAULT) {
|
||||
in(d); ps("default:"); nl();
|
||||
pstmlist(d, stm->a);
|
||||
}
|
||||
list = list->b;
|
||||
}
|
||||
}
|
||||
|
||||
static void pstm(int d, js_Ast *stm)
|
||||
{
|
||||
if (stm->type == STM_BLOCK) {
|
||||
pblock(d, stm);
|
||||
return;
|
||||
}
|
||||
|
||||
in(d);
|
||||
|
||||
switch (stm->type) {
|
||||
case AST_FUNDEC:
|
||||
ps("function ");
|
||||
pexp(d, stm->a);
|
||||
pc('(');
|
||||
pargs(d, stm->b);
|
||||
pc(')'); sp(); pc('{'); nl();
|
||||
pstmlist(d, stm->c);
|
||||
in(d); pc('}');
|
||||
break;
|
||||
|
||||
case STM_EMPTY:
|
||||
pc(';');
|
||||
break;
|
||||
|
||||
case STM_VAR:
|
||||
ps("var ");
|
||||
pvarlist(d, stm->a);
|
||||
pc(';');
|
||||
break;
|
||||
|
||||
case STM_IF:
|
||||
ps("if"); sp(); pc('('); pexp(d, stm->a); pc(')');
|
||||
pstmh(d, stm->b);
|
||||
if (stm->c) {
|
||||
nl(); in(d); ps("else");
|
||||
pstmh(d, stm->c);
|
||||
}
|
||||
break;
|
||||
|
||||
case STM_DO:
|
||||
ps("do");
|
||||
pstmh(d, stm->a);
|
||||
nl();
|
||||
in(d); ps("while"); sp(); pc('('); pexp(d, stm->b); pc(')'); pc(';');
|
||||
break;
|
||||
|
||||
case STM_WHILE:
|
||||
ps("while"); sp(); pc('('); pexp(d, stm->a); pc(')');
|
||||
pstmh(d, stm->b);
|
||||
break;
|
||||
|
||||
case STM_FOR:
|
||||
ps("for"); sp(); pc('(');
|
||||
pexp(d, stm->a); pc(';'); sp();
|
||||
pexp(d, stm->b); pc(';'); sp();
|
||||
pexp(d, stm->c); pc(')');
|
||||
pstmh(d, stm->d);
|
||||
break;
|
||||
case STM_FOR_VAR:
|
||||
ps("for"); sp(); ps("(var ");
|
||||
pvarlist(d, stm->a); pc(';'); sp();
|
||||
pexp(d, stm->b); pc(';'); sp();
|
||||
pexp(d, stm->c); pc(')');
|
||||
pstmh(d, stm->d);
|
||||
break;
|
||||
case STM_FOR_IN:
|
||||
ps("for"); sp(); pc('(');
|
||||
pexp(d, stm->a); ps(" in ");
|
||||
pexp(d, stm->b); pc(')');
|
||||
pstmh(d, stm->c);
|
||||
break;
|
||||
case STM_FOR_IN_VAR:
|
||||
ps("for"); sp(); ps("(var ");
|
||||
pvarlist(d, stm->a); ps(" in ");
|
||||
pexp(d, stm->b); pc(')');
|
||||
pstmh(d, stm->c);
|
||||
break;
|
||||
|
||||
case STM_CONTINUE:
|
||||
ps("continue");
|
||||
if (stm->a) {
|
||||
pc(' '); pexp(d, stm->a);
|
||||
}
|
||||
pc(';');
|
||||
break;
|
||||
|
||||
case STM_BREAK:
|
||||
ps("break");
|
||||
if (stm->a) {
|
||||
pc(' '); pexp(d, stm->a);
|
||||
}
|
||||
pc(';');
|
||||
break;
|
||||
|
||||
case STM_RETURN:
|
||||
ps("return");
|
||||
if (stm->a) {
|
||||
pc(' '); pexp(d, stm->a);
|
||||
}
|
||||
pc(';');
|
||||
break;
|
||||
|
||||
case STM_WITH:
|
||||
ps("with"); sp(); pc('('); pexp(d, stm->a); pc(')');
|
||||
pstmh(d, stm->b);
|
||||
break;
|
||||
|
||||
case STM_SWITCH:
|
||||
ps("switch"); sp(); pc('(');
|
||||
pexp(d, stm->a);
|
||||
pc(')'); sp(); pc('{'); nl();
|
||||
pcaselist(d, stm->b);
|
||||
in(d); pc('}');
|
||||
break;
|
||||
|
||||
case STM_THROW:
|
||||
ps("throw "); pexp(d, stm->a); pc(';');
|
||||
break;
|
||||
|
||||
case STM_TRY:
|
||||
ps("try");
|
||||
if (minify && stm->a->type != STM_BLOCK)
|
||||
pc(' ');
|
||||
pstmh(d, stm->a);
|
||||
if (stm->b && stm->c) {
|
||||
nl(); in(d); ps("catch"); sp(); pc('('); pexp(d, stm->b); pc(')');
|
||||
pstmh(d, stm->c);
|
||||
}
|
||||
if (stm->d) {
|
||||
nl(); in(d); ps("finally");
|
||||
pstmh(d, stm->d);
|
||||
}
|
||||
break;
|
||||
|
||||
case STM_LABEL:
|
||||
pexp(d, stm->a); pc(':'); sp(); pstm(d, stm->b);
|
||||
break;
|
||||
|
||||
case STM_DEBUGGER:
|
||||
ps("debugger");
|
||||
pc(';');
|
||||
break;
|
||||
|
||||
default:
|
||||
pexp(d, stm);
|
||||
pc(';');
|
||||
}
|
||||
}
|
||||
|
||||
static void pstmlist(int d, js_Ast *list)
|
||||
{
|
||||
while (list) {
|
||||
assert(list->type == AST_LIST);
|
||||
pstm(d+1, list->a);
|
||||
nl();
|
||||
list = list->b;
|
||||
}
|
||||
}
|
||||
|
||||
static void jsP_dumpsyntax(js_State *J, js_Ast *prog)
|
||||
{
|
||||
if (prog) {
|
||||
if (prog->type == AST_LIST)
|
||||
pstmlist(-1, prog);
|
||||
else {
|
||||
pstm(0, prog);
|
||||
nl();
|
||||
}
|
||||
}
|
||||
if (minify > 1)
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
/* S-expression list representation */
|
||||
|
||||
static void snode(int d, js_Ast *node)
|
||||
{
|
||||
void (*afun)(int,js_Ast*) = snode;
|
||||
void (*bfun)(int,js_Ast*) = snode;
|
||||
void (*cfun)(int,js_Ast*) = snode;
|
||||
void (*dfun)(int,js_Ast*) = snode;
|
||||
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (node->type == AST_LIST) {
|
||||
slist(d, node);
|
||||
return;
|
||||
}
|
||||
|
||||
pc('(');
|
||||
ps(astname[node->type]);
|
||||
switch (node->type) {
|
||||
default: break;
|
||||
case AST_IDENTIFIER: pc(' '); ps(node->string); break;
|
||||
case EXP_IDENTIFIER: pc(' '); ps(node->string); break;
|
||||
case EXP_STRING: pc(' '); pstr(node->string); break;
|
||||
case EXP_REGEXP: pc(' '); pregexp(node->string, node->number); break;
|
||||
case EXP_NUMBER: printf(" %.9g", node->number); break;
|
||||
case STM_BLOCK: afun = sblock; break;
|
||||
case AST_FUNDEC: case EXP_FUN: cfun = sblock; break;
|
||||
case EXP_PROP_GET: cfun = sblock; break;
|
||||
case EXP_PROP_SET: cfun = sblock; break;
|
||||
case STM_SWITCH: bfun = sblock; break;
|
||||
case STM_CASE: bfun = sblock; break;
|
||||
case STM_DEFAULT: afun = sblock; break;
|
||||
}
|
||||
if (node->a) { pc(' '); afun(d, node->a); }
|
||||
if (node->b) { pc(' '); bfun(d, node->b); }
|
||||
if (node->c) { pc(' '); cfun(d, node->c); }
|
||||
if (node->d) { pc(' '); dfun(d, node->d); }
|
||||
pc(')');
|
||||
}
|
||||
|
||||
static void slist(int d, js_Ast *list)
|
||||
{
|
||||
pc('[');
|
||||
while (list) {
|
||||
assert(list->type == AST_LIST);
|
||||
snode(d, list->a);
|
||||
list = list->b;
|
||||
if (list)
|
||||
pc(' ');
|
||||
}
|
||||
pc(']');
|
||||
}
|
||||
|
||||
static void sblock(int d, js_Ast *list)
|
||||
{
|
||||
ps("[\n");
|
||||
in(d+1);
|
||||
while (list) {
|
||||
assert(list->type == AST_LIST);
|
||||
snode(d+1, list->a);
|
||||
list = list->b;
|
||||
if (list) {
|
||||
nl();
|
||||
in(d+1);
|
||||
}
|
||||
}
|
||||
nl(); in(d); pc(']');
|
||||
}
|
||||
|
||||
static void jsP_dumplist(js_State *J, js_Ast *prog)
|
||||
{
|
||||
if (prog) {
|
||||
if (prog->type == AST_LIST)
|
||||
sblock(0, prog);
|
||||
else
|
||||
snode(0, prog);
|
||||
nl();
|
||||
}
|
||||
}
|
||||
|
||||
static void js_ppstring(js_State *J, const char *filename, const char *source)
|
||||
{
|
||||
js_Ast *P;
|
||||
js_Function *F;
|
||||
|
||||
if (js_try(J)) {
|
||||
jsP_freeparse(J);
|
||||
js_throw(J);
|
||||
}
|
||||
|
||||
P = jsP_parse(J, filename, source);
|
||||
F = jsC_compilescript(J, P, J->default_strict);
|
||||
|
||||
switch (format) {
|
||||
case 0:
|
||||
jsP_dumpsyntax(J, P);
|
||||
break;
|
||||
case 1:
|
||||
jsP_dumplist(J, P);
|
||||
break;
|
||||
case 2:
|
||||
jsC_dumpfunction(J, F);
|
||||
break;
|
||||
}
|
||||
|
||||
jsP_freeparse(J);
|
||||
js_endtry(J);
|
||||
}
|
||||
|
||||
static void js_ppfile(js_State *J, const char *filename)
|
||||
{
|
||||
FILE * volatile f = NULL;
|
||||
char * volatile s = NULL;
|
||||
int n, t;
|
||||
|
||||
if (js_try(J)) {
|
||||
js_free(J, s);
|
||||
if (f) fclose(f);
|
||||
js_throw(J);
|
||||
}
|
||||
|
||||
f = fopen(filename, "rb");
|
||||
if (!f) {
|
||||
js_error(J, "cannot open file: '%s'", filename);
|
||||
}
|
||||
|
||||
if (fseek(f, 0, SEEK_END) < 0) {
|
||||
js_error(J, "cannot seek in file: '%s'", filename);
|
||||
}
|
||||
|
||||
n = ftell(f);
|
||||
if (n < 0) {
|
||||
js_error(J, "cannot tell in file: '%s'", filename);
|
||||
}
|
||||
|
||||
if (fseek(f, 0, SEEK_SET) < 0) {
|
||||
js_error(J, "cannot seek in file: '%s'", filename);
|
||||
}
|
||||
|
||||
s = js_malloc(J, n + 1); /* add space for string terminator */
|
||||
if (!s) {
|
||||
js_error(J, "cannot allocate storage for file contents: '%s'", filename);
|
||||
}
|
||||
|
||||
t = fread(s, 1, (size_t)n, f);
|
||||
if (t != n) {
|
||||
js_error(J, "cannot read data from file: '%s'", filename);
|
||||
}
|
||||
|
||||
s[n] = 0; /* zero-terminate string containing file data */
|
||||
|
||||
js_ppstring(J, filename, s);
|
||||
|
||||
js_endtry(J);
|
||||
js_free(J, s);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
static void js_tryppfile(js_State *J, const char *file)
|
||||
{
|
||||
if (js_try(J)) {
|
||||
js_report(J, js_trystring(J, -1, "Error"));
|
||||
js_pop(J, 1);
|
||||
return;
|
||||
}
|
||||
js_ppfile(J, file);
|
||||
js_endtry(J);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
js_State *J;
|
||||
int i;
|
||||
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "usage: mujs-pp [-m | -mm | -s | -c] input.js\n");
|
||||
fprintf(stderr, " -m\tminify output\n");
|
||||
fprintf(stderr, " -mm\tminify output more\n");
|
||||
fprintf(stderr, " -s\tprint syntax tree\n");
|
||||
fprintf(stderr, " -c\tprint bytecode\n");
|
||||
}
|
||||
|
||||
J = js_newstate(NULL, NULL, 0);
|
||||
|
||||
for (i = 1; i < argc; ++i) {
|
||||
if (!strcmp(argv[i], "-m"))
|
||||
format = 0, minify = 1;
|
||||
else if (!strcmp(argv[i], "-mm"))
|
||||
format = 0, minify = 2;
|
||||
else if (!strcmp(argv[i], "-s"))
|
||||
format = 1, minify = 0;
|
||||
else if (!strcmp(argv[i], "-c"))
|
||||
format = 2, minify = 0;
|
||||
else
|
||||
js_tryppfile(J, argv[i]);
|
||||
}
|
||||
|
||||
js_gc(J, 0);
|
||||
js_freestate(J);
|
||||
|
||||
return 0;
|
||||
}
|
1277
src/mujs/regexp.c
Normal file
1277
src/mujs/regexp.c
Normal file
File diff suppressed because it is too large
Load diff
46
src/mujs/regexp.h
Normal file
46
src/mujs/regexp.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
#ifndef regexp_h
|
||||
#define regexp_h
|
||||
|
||||
#define regcompx js_regcompx
|
||||
#define regfreex js_regfreex
|
||||
#define regcomp js_regcomp
|
||||
#define regexec js_regexec
|
||||
#define regfree js_regfree
|
||||
|
||||
typedef struct Reprog Reprog;
|
||||
typedef struct Resub Resub;
|
||||
|
||||
Reprog *regcompx(void *(*alloc)(void *ctx, void *p, int n), void *ctx,
|
||||
const char *pattern, int cflags, const char **errorp);
|
||||
void regfreex(void *(*alloc)(void *ctx, void *p, int n), void *ctx,
|
||||
Reprog *prog);
|
||||
|
||||
Reprog *regcomp(const char *pattern, int cflags, const char **errorp);
|
||||
int regexec(Reprog *prog, const char *string, Resub *sub, int eflags);
|
||||
void regfree(Reprog *prog);
|
||||
|
||||
enum {
|
||||
/* regcomp flags */
|
||||
REG_ICASE = 1,
|
||||
REG_NEWLINE = 2,
|
||||
|
||||
/* regexec flags */
|
||||
REG_NOTBOL = 4,
|
||||
};
|
||||
|
||||
/* If you redefine REG_MAXSUB, you must make sure both the calling
|
||||
* code and the regexp.c compilation unit use the same value!
|
||||
*/
|
||||
#ifndef REG_MAXSUB
|
||||
#define REG_MAXSUB 16
|
||||
#endif
|
||||
|
||||
struct Resub {
|
||||
int nsub;
|
||||
struct {
|
||||
const char *sp;
|
||||
const char *ep;
|
||||
} sub[REG_MAXSUB];
|
||||
};
|
||||
|
||||
#endif
|
139
src/mujs/tools/test262
Executable file
139
src/mujs/tools/test262
Executable file
|
@ -0,0 +1,139 @@
|
|||
#!/bin/sh
|
||||
|
||||
usage() {
|
||||
[ "${1-}" ] && { to=2; >&$to printf "Error: %s\n" "$1"; } || to=1
|
||||
>&$to echo "Usage: ${0##*/} [OPTIONS] test-file | test-dir"
|
||||
>&$to echo "Run test-262 ES5 test file or directory (at the test262 dir)."
|
||||
>&$to echo " -s Print source code of failed tests."
|
||||
>&$to echo " -p Print every test name before running it"
|
||||
>&$to echo " -f Display full paths and full stack trace when possible"
|
||||
>&$to echo " -l file.js Load file.js after the harness and before the test (up to one)"
|
||||
>&$to echo " -m MUJS MUJS is [path/to/]mujs binary to test"
|
||||
>&$to echo " Default is $(dirname "$0")/../build/release/mujs or mujs at \$PATH"
|
||||
>&$to echo " -b Don't skip known bad (crashing/hanging) tests"
|
||||
>&$to echo " -B Run only known bad tests"
|
||||
exit $((to-1))
|
||||
}
|
||||
|
||||
KNOWN_BAD="
|
||||
--hang-with-sta.js:
|
||||
S15.1.3.2_A2.5_T1.js
|
||||
S15.1.3.1_A2.5_T1.js
|
||||
|
||||
--Hang-(or-taking-more-than-few-seconds):
|
||||
15.4.4.18-3-14.js
|
||||
15.4.4.20-3-14.js
|
||||
S15.4.4.10_A3_T2.js
|
||||
S15.4.4.10_A3_T1.js
|
||||
15.4.4.19-3-29.js
|
||||
15.4.4.19-3-28.js
|
||||
15.4.4.19-3-8.js
|
||||
15.4.4.19-3-14.js
|
||||
S15.4.4.8_A3_T3.js
|
||||
15.4.4.22-3-9.js
|
||||
15.4.4.22-3-7.js
|
||||
15.4.4.22-3-25.js
|
||||
15.4.4.22-3-14.js
|
||||
15.4.4.21-3-14.js
|
||||
15.4.4.15-3-28.js
|
||||
15.4.4.15-3-14.js
|
||||
15.4.4.15-3-7.js
|
||||
15.4.4.15-3-25.js
|
||||
15.4.4.15-3-9.js
|
||||
"
|
||||
|
||||
SKIP_KNOWN=yes # "yes": skip bad "no": don't skip "neg": run only bad
|
||||
PRINT_ALL=
|
||||
EXTRA_ARGS=
|
||||
mujs= lopt=
|
||||
|
||||
while getopts bBfhl:ps o; do
|
||||
case $o in
|
||||
h) usage ;;
|
||||
b) SKIP_KNOWN=no ;;
|
||||
B) SKIP_KNOWN=neg ;;
|
||||
p) PRINT_ALL=yes ;;
|
||||
s) EXTRA_ARGS="$EXTRA_ARGS -s" ;;
|
||||
f) EXTRA_ARGS="$EXTRA_ARGS -f" ;;
|
||||
l) [ "$OPTARG" ] && lopt=$OPTARG || usage "empty file for -l" ;;
|
||||
m) mujs=$OPTARG;;
|
||||
*) usage "unknown option -$o" ;;
|
||||
esac
|
||||
done
|
||||
shift $((OPTIND-1))
|
||||
[ $# = 1 ] || usage "expecting one file/dir"
|
||||
|
||||
BAD=
|
||||
if [ "$SKIP_KNOWN" != no ]; then
|
||||
for b in $KNOWN_BAD; do
|
||||
BAD="$BAD $b "
|
||||
done
|
||||
fi
|
||||
|
||||
find_root() {
|
||||
ROOT=$1
|
||||
n=0
|
||||
while ! [ -e "$ROOT"/test/harness/sta.js ]; do
|
||||
ROOT=$ROOT/..
|
||||
n=$((n+1))
|
||||
[ $n -lt 10 ] || usage "can't find test-suite root"
|
||||
done
|
||||
}
|
||||
|
||||
if [ -d "$1" ]; then
|
||||
find_root "$1"
|
||||
|
||||
if [ "$ROOT" = "$1" ]; then
|
||||
FILES_CMD='find "$1/test/suite" -name "*.js" | sort -V'
|
||||
else
|
||||
FILES_CMD='find "$1" -name "*.js" | sort -V'
|
||||
fi
|
||||
else
|
||||
find_root "$(dirname "$1")"
|
||||
FILES_CMD='printf "%s\n" "$1"'
|
||||
fi
|
||||
|
||||
if ! [ "$mujs" ]; then
|
||||
# try to use a recently built mujs rather than a global one
|
||||
mujs=$(dirname "$0")/../build/release/mujs
|
||||
[ -e "$mujs" ] || mujs=mujs
|
||||
fi
|
||||
jsharness=$(dirname "$0")/test262-harness.js
|
||||
|
||||
total=0
|
||||
skipped=0
|
||||
failed=0
|
||||
|
||||
eval "$FILES_CMD" | (
|
||||
while IFS= read -r f && [ "$f" ]; do
|
||||
total=$((total+1))
|
||||
base=${f##*/}
|
||||
|
||||
case $BAD in *" $base "*) bad=yes;; *) bad=no;; esac
|
||||
|
||||
case $bad-$SKIP_KNOWN in
|
||||
yes-yes)
|
||||
skipped=$((skipped+1))
|
||||
printf "[Skipping: $base]\n\n"
|
||||
;;
|
||||
no-neg) # not known bad and running only bad - don't print anything
|
||||
skipped=$((skipped+1))
|
||||
;;
|
||||
*)
|
||||
[ "$PRINT_ALL" ] && echo "Testing: $f"
|
||||
if ! "$mujs" -- "$jsharness" $EXTRA_ARGS ${lopt:+-l "$lopt"} "$ROOT" "$f" 2>&1; then
|
||||
failed=$((failed+1))
|
||||
echo
|
||||
fi
|
||||
esac
|
||||
done
|
||||
|
||||
if [ $total -gt 1 ]; then
|
||||
printf "Total: $total\n"
|
||||
printf "Pass: %${#total}s\n" $((total - skipped - failed))
|
||||
printf "Skip: %${#total}s\n" $skipped
|
||||
printf "Fail: %${#total}s\n" $failed
|
||||
fi
|
||||
|
||||
[ "$failed" = 0 ]
|
||||
)
|
152
src/mujs/tools/test262-harness.js
Normal file
152
src/mujs/tools/test262-harness.js
Normal file
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* Runs one test file from the ES5 test suite test-262
|
||||
* Usage: mujs <this-file> [-s ] [-f] [-l file1.js -l ...] suit-root test-file
|
||||
* -s: print test source on failure
|
||||
* -f: print full paths/stacktraces if possible
|
||||
* -l: load a js file after the harness and before the test (to override things)
|
||||
*
|
||||
* If there are errors, print them and exits with code 1, else exit code is 0.
|
||||
*
|
||||
* The test suite is at: https://github.com/tc39/test262.git
|
||||
* The ES5 suite is at branch "es5-tests"
|
||||
*
|
||||
* - The test suite throws on any error, possibly with info at ex.message .
|
||||
* - Some tests make irreversible changes to global attrubutes, therefore it's
|
||||
* required to run each test file in a new mujs instance.
|
||||
*/
|
||||
|
||||
(function(global) {
|
||||
"use strict";
|
||||
|
||||
// clean the global environment
|
||||
var mujs = {};
|
||||
|
||||
["gc", "load", "compile", "print", "write", "read", "readline", "quit", "scriptArgs"]
|
||||
.forEach(function(a) {
|
||||
mujs[a] = global[a];
|
||||
delete global[a];
|
||||
});
|
||||
|
||||
// restore the original Error.toString behavior - it's being tested too
|
||||
Error.prototype.toString = function() {
|
||||
return this.name + ': ' + this.message;
|
||||
}
|
||||
|
||||
function die_usage(str) {
|
||||
if (str)
|
||||
mujs.print(str);
|
||||
mujs.print("Usage: mujs <this-file> [-f] [-l file1.js -l ...] suit-root test-file");
|
||||
mujs.quit(1);
|
||||
}
|
||||
|
||||
// our file loader
|
||||
function load(str, as_filename) {
|
||||
try {
|
||||
var runtime_err = false;
|
||||
var compiled = mujs.compile(str, as_filename);
|
||||
runtime_err = true;
|
||||
compiled();
|
||||
return false;
|
||||
} catch (e) {
|
||||
return {err: e, runtime: runtime_err};
|
||||
}
|
||||
}
|
||||
|
||||
var args = mujs.scriptArgs;
|
||||
var full_mode = false;
|
||||
var print_src = false;
|
||||
var overrides = [];
|
||||
while ((""+args[0])[0] == "-") {
|
||||
switch (args[0]) {
|
||||
case "-f": full_mode = true;
|
||||
break;
|
||||
case "-s": print_src = true;
|
||||
break;
|
||||
case "-l": args.shift();
|
||||
overrides.push(args[0]);
|
||||
break;
|
||||
default: die_usage("Unknown option " + args[0]);
|
||||
}
|
||||
args.shift();
|
||||
}
|
||||
if (args.length != 2)
|
||||
die_usage("Exactly 2 paths are expected");
|
||||
var root_path = args[0];
|
||||
var test_path = args[1];
|
||||
|
||||
// load suite utils
|
||||
["sta.js", "testBuiltInObject.js", "testIntl.js"]
|
||||
.forEach(function(u) {
|
||||
var path = root_path + "/test/harness/" + u;
|
||||
var as_file = full_mode ? path : "test/harness/" + u;
|
||||
var err = load(mujs.read(path), as_file);
|
||||
if (err) throw (err.err);
|
||||
});
|
||||
|
||||
// load user overrides (e.g. reduced getPrecision), with a global mujs
|
||||
if (overrides.length) {
|
||||
global.mujs = mujs
|
||||
overrides.forEach(function(f) {
|
||||
var err = load(mujs.read(f), f);
|
||||
if (err) throw (err.err);
|
||||
});
|
||||
delete global.mujs;
|
||||
}
|
||||
|
||||
// the actual test
|
||||
var source = mujs.read(test_path);
|
||||
var negative = !!source.match(/@negative/);
|
||||
if (negative)
|
||||
var neg_str = (source.match(/@negative (.*)/) || [])[1];
|
||||
var as_file = test_path;
|
||||
if (!full_mode) {
|
||||
as_file = test_path.replace(/\\/g, "/");
|
||||
var sub = as_file.indexOf("/suite/");
|
||||
if (sub >= 0)
|
||||
as_file = "test" + as_file.substring(sub);
|
||||
}
|
||||
|
||||
var result = load(mujs.read(test_path), as_file);
|
||||
if (!!result == negative) {
|
||||
// The docs don't really help about matching str, but this covers all cases
|
||||
if (neg_str)
|
||||
var err_for_match = /NotEarlyError/.test(neg_str) ? result.err.message : result.err.name;
|
||||
if (!negative || !neg_str || RegExp(neg_str).exec(err_for_match))
|
||||
mujs.quit(0);
|
||||
}
|
||||
|
||||
// failed
|
||||
// FIXME: @description can span lines. E.g. test/suite/bestPractice/Sbp_A3_T2.js
|
||||
var desc = source.match(/@description (.*)/);
|
||||
var info = "[File] " + as_file +
|
||||
(desc ? "\n[Desc] " + desc[1] : "") +
|
||||
"\n";
|
||||
|
||||
if (result) {
|
||||
var err = result.err;
|
||||
var msg = !neg_str ? err : "[Mismatch @negative " + neg_str + "]" + "\n " + err;
|
||||
|
||||
info += (result.runtime ? "[run] " : "[load] ") + msg;
|
||||
if (err && err.stackTrace && (result.runtime || full_mode)) {
|
||||
if (full_mode) {
|
||||
info += err.stackTrace;
|
||||
} else {
|
||||
// trim the internal loader from the trace
|
||||
var internal = err.stackTrace.indexOf("\n" + load("mujs_blahblah()").err.stackTrace.trim().split("\n")[1]);
|
||||
if (internal >= 0)
|
||||
info += err.stackTrace.substring(0, internal);
|
||||
else
|
||||
info += err.stackTrace;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
info += "[run] [Error expected but none thrown]";
|
||||
}
|
||||
|
||||
if (print_src)
|
||||
info += "\n[Source]\n" + source;
|
||||
|
||||
mujs.print(info);
|
||||
mujs.quit(1);
|
||||
|
||||
})(this)
|
305
src/mujs/utf.c
Normal file
305
src/mujs/utf.c
Normal file
|
@ -0,0 +1,305 @@
|
|||
/*
|
||||
* The authors of this software are Rob Pike and Ken Thompson.
|
||||
* Copyright (c) 2002 by Lucent Technologies.
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose without fee is hereby granted, provided that this entire notice
|
||||
* is included in all copies of any software which is or includes a copy
|
||||
* or modification of this software and in all copies of the supporting
|
||||
* documentation for such software.
|
||||
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
|
||||
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
|
||||
* ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
|
||||
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "utf.h"
|
||||
#include "utfdata.h"
|
||||
|
||||
#define nelem(a) (int)(sizeof (a) / sizeof (a)[0])
|
||||
|
||||
typedef unsigned char uchar;
|
||||
|
||||
enum
|
||||
{
|
||||
Bit1 = 7,
|
||||
Bitx = 6,
|
||||
Bit2 = 5,
|
||||
Bit3 = 4,
|
||||
Bit4 = 3,
|
||||
Bit5 = 2,
|
||||
|
||||
T1 = ((1<<(Bit1+1))-1) ^ 0xFF, /* 0000 0000 */
|
||||
Tx = ((1<<(Bitx+1))-1) ^ 0xFF, /* 1000 0000 */
|
||||
T2 = ((1<<(Bit2+1))-1) ^ 0xFF, /* 1100 0000 */
|
||||
T3 = ((1<<(Bit3+1))-1) ^ 0xFF, /* 1110 0000 */
|
||||
T4 = ((1<<(Bit4+1))-1) ^ 0xFF, /* 1111 0000 */
|
||||
T5 = ((1<<(Bit5+1))-1) ^ 0xFF, /* 1111 1000 */
|
||||
|
||||
Rune1 = (1<<(Bit1+0*Bitx))-1, /* 0000 0000 0000 0000 0111 1111 */
|
||||
Rune2 = (1<<(Bit2+1*Bitx))-1, /* 0000 0000 0000 0111 1111 1111 */
|
||||
Rune3 = (1<<(Bit3+2*Bitx))-1, /* 0000 0000 1111 1111 1111 1111 */
|
||||
Rune4 = (1<<(Bit4+3*Bitx))-1, /* 0001 1111 1111 1111 1111 1111 */
|
||||
|
||||
Maskx = (1<<Bitx)-1, /* 0011 1111 */
|
||||
Testx = Maskx ^ 0xFF, /* 1100 0000 */
|
||||
|
||||
Bad = Runeerror
|
||||
};
|
||||
|
||||
int
|
||||
chartorune(Rune *rune, const char *str)
|
||||
{
|
||||
int c, c1, c2, c3;
|
||||
int l;
|
||||
|
||||
/* overlong null character */
|
||||
if((uchar)str[0] == 0xc0 && (uchar)str[1] == 0x80) {
|
||||
*rune = 0;
|
||||
return 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* one character sequence
|
||||
* 00000-0007F => T1
|
||||
*/
|
||||
c = *(uchar*)str;
|
||||
if(c < Tx) {
|
||||
*rune = c;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* two character sequence
|
||||
* 0080-07FF => T2 Tx
|
||||
*/
|
||||
c1 = *(uchar*)(str+1) ^ Tx;
|
||||
if(c1 & Testx)
|
||||
goto bad;
|
||||
if(c < T3) {
|
||||
if(c < T2)
|
||||
goto bad;
|
||||
l = ((c << Bitx) | c1) & Rune2;
|
||||
if(l <= Rune1)
|
||||
goto bad;
|
||||
*rune = l;
|
||||
return 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* three character sequence
|
||||
* 0800-FFFF => T3 Tx Tx
|
||||
*/
|
||||
c2 = *(uchar*)(str+2) ^ Tx;
|
||||
if(c2 & Testx)
|
||||
goto bad;
|
||||
if(c < T4) {
|
||||
l = ((((c << Bitx) | c1) << Bitx) | c2) & Rune3;
|
||||
if(l <= Rune2)
|
||||
goto bad;
|
||||
*rune = l;
|
||||
return 3;
|
||||
}
|
||||
|
||||
/*
|
||||
* four character sequence
|
||||
* 10000-10FFFF => T4 Tx Tx Tx
|
||||
*/
|
||||
if(UTFmax >= 4) {
|
||||
c3 = *(uchar*)(str+3) ^ Tx;
|
||||
if(c3 & Testx)
|
||||
goto bad;
|
||||
if(c < T5) {
|
||||
l = ((((((c << Bitx) | c1) << Bitx) | c2) << Bitx) | c3) & Rune4;
|
||||
if(l <= Rune3)
|
||||
goto bad;
|
||||
if(l > Runemax)
|
||||
goto bad;
|
||||
*rune = l;
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* bad decoding
|
||||
*/
|
||||
bad:
|
||||
*rune = Bad;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
runetochar(char *str, const Rune *rune)
|
||||
{
|
||||
int c = *rune;
|
||||
|
||||
/* overlong null character */
|
||||
if (c == 0) {
|
||||
str[0] = (char)0xc0;
|
||||
str[1] = (char)0x80;
|
||||
return 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* one character sequence
|
||||
* 00000-0007F => 00-7F
|
||||
*/
|
||||
if(c <= Rune1) {
|
||||
str[0] = c;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* two character sequence
|
||||
* 00080-007FF => T2 Tx
|
||||
*/
|
||||
if(c <= Rune2) {
|
||||
str[0] = T2 | (c >> 1*Bitx);
|
||||
str[1] = Tx | (c & Maskx);
|
||||
return 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* three character sequence
|
||||
* 00800-0FFFF => T3 Tx Tx
|
||||
*/
|
||||
if(c > Runemax)
|
||||
c = Runeerror;
|
||||
if(c <= Rune3) {
|
||||
str[0] = T3 | (c >> 2*Bitx);
|
||||
str[1] = Tx | ((c >> 1*Bitx) & Maskx);
|
||||
str[2] = Tx | (c & Maskx);
|
||||
return 3;
|
||||
}
|
||||
|
||||
/*
|
||||
* four character sequence
|
||||
* 010000-1FFFFF => T4 Tx Tx Tx
|
||||
*/
|
||||
str[0] = T4 | (c >> 3*Bitx);
|
||||
str[1] = Tx | ((c >> 2*Bitx) & Maskx);
|
||||
str[2] = Tx | ((c >> 1*Bitx) & Maskx);
|
||||
str[3] = Tx | (c & Maskx);
|
||||
return 4;
|
||||
}
|
||||
|
||||
int
|
||||
runelen(int c)
|
||||
{
|
||||
Rune rune;
|
||||
char str[10];
|
||||
|
||||
rune = c;
|
||||
return runetochar(str, &rune);
|
||||
}
|
||||
|
||||
static const Rune *
|
||||
ucd_bsearch(Rune c, const Rune *t, int n, int ne)
|
||||
{
|
||||
const Rune *p;
|
||||
int m;
|
||||
|
||||
while(n > 1) {
|
||||
m = n/2;
|
||||
p = t + m*ne;
|
||||
if(c >= p[0]) {
|
||||
t = p;
|
||||
n = n-m;
|
||||
} else
|
||||
n = m;
|
||||
}
|
||||
if(n && c >= t[0])
|
||||
return t;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Rune
|
||||
tolowerrune(Rune c)
|
||||
{
|
||||
const Rune *p;
|
||||
|
||||
p = ucd_bsearch(c, ucd_tolower2, nelem(ucd_tolower2)/3, 3);
|
||||
if(p && c >= p[0] && c <= p[1])
|
||||
return c + p[2];
|
||||
p = ucd_bsearch(c, ucd_tolower1, nelem(ucd_tolower1)/2, 2);
|
||||
if(p && c == p[0])
|
||||
return c + p[1];
|
||||
return c;
|
||||
}
|
||||
|
||||
Rune
|
||||
toupperrune(Rune c)
|
||||
{
|
||||
const Rune *p;
|
||||
|
||||
p = ucd_bsearch(c, ucd_toupper2, nelem(ucd_toupper2)/3, 3);
|
||||
if(p && c >= p[0] && c <= p[1])
|
||||
return c + p[2];
|
||||
p = ucd_bsearch(c, ucd_toupper1, nelem(ucd_toupper1)/2, 2);
|
||||
if(p && c == p[0])
|
||||
return c + p[1];
|
||||
return c;
|
||||
}
|
||||
|
||||
int
|
||||
islowerrune(Rune c)
|
||||
{
|
||||
const Rune *p;
|
||||
|
||||
p = ucd_bsearch(c, ucd_toupper2, nelem(ucd_toupper2)/3, 3);
|
||||
if(p && c >= p[0] && c <= p[1])
|
||||
return 1;
|
||||
p = ucd_bsearch(c, ucd_toupper1, nelem(ucd_toupper1)/2, 2);
|
||||
if(p && c == p[0])
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
isupperrune(Rune c)
|
||||
{
|
||||
const Rune *p;
|
||||
|
||||
p = ucd_bsearch(c, ucd_tolower2, nelem(ucd_tolower2)/3, 3);
|
||||
if(p && c >= p[0] && c <= p[1])
|
||||
return 1;
|
||||
p = ucd_bsearch(c, ucd_tolower1, nelem(ucd_tolower1)/2, 2);
|
||||
if(p && c == p[0])
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
isalpharune(Rune c)
|
||||
{
|
||||
const Rune *p;
|
||||
|
||||
p = ucd_bsearch(c, ucd_alpha2, nelem(ucd_alpha2)/2, 2);
|
||||
if(p && c >= p[0] && c <= p[1])
|
||||
return 1;
|
||||
p = ucd_bsearch(c, ucd_alpha1, nelem(ucd_alpha1), 1);
|
||||
if(p && c == p[0])
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const Rune *
|
||||
tolowerrune_full(Rune c)
|
||||
{
|
||||
const Rune *p;
|
||||
p = ucd_bsearch(c, ucd_tolower_full, nelem(ucd_tolower_full)/4, 4);
|
||||
if(p && c == p[0])
|
||||
return p + 1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const Rune *
|
||||
toupperrune_full(Rune c)
|
||||
{
|
||||
const Rune *p;
|
||||
p = ucd_bsearch(c, ucd_toupper_full, nelem(ucd_toupper_full)/5, 5);
|
||||
if(p && c == p[0])
|
||||
return p + 1;
|
||||
return NULL;
|
||||
}
|
52
src/mujs/utf.h
Normal file
52
src/mujs/utf.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* The authors of this software are Rob Pike and Ken Thompson.
|
||||
* Copyright (c) 2002 by Lucent Technologies.
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose without fee is hereby granted, provided that this entire notice
|
||||
* is included in all copies of any software which is or includes a copy
|
||||
* or modification of this software and in all copies of the supporting
|
||||
* documentation for such software.
|
||||
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
|
||||
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
|
||||
* ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
|
||||
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
|
||||
*/
|
||||
#ifndef js_utf_h
|
||||
#define js_utf_h
|
||||
|
||||
typedef int Rune; /* 32 bits */
|
||||
|
||||
#define chartorune jsU_chartorune
|
||||
#define runetochar jsU_runetochar
|
||||
#define runelen jsU_runelen
|
||||
|
||||
#define isalpharune jsU_isalpharune
|
||||
#define islowerrune jsU_islowerrune
|
||||
#define isupperrune jsU_isupperrune
|
||||
#define tolowerrune jsU_tolowerrune
|
||||
#define toupperrune jsU_toupperrune
|
||||
#define tolowerrune_full jsU_tolowerrune_full
|
||||
#define toupperrune_full jsU_toupperrune_full
|
||||
|
||||
enum
|
||||
{
|
||||
UTFmax = 4, /* maximum bytes per rune */
|
||||
Runesync = 0x80, /* cannot represent part of a UTF sequence (<) */
|
||||
Runeself = 0x80, /* rune and UTF sequences are the same (<) */
|
||||
Runeerror = 0xFFFD, /* decoding error in UTF */
|
||||
Runemax = 0x10FFFF, /* maximum rune value */
|
||||
};
|
||||
|
||||
int chartorune(Rune *rune, const char *str);
|
||||
int runetochar(char *str, const Rune *rune);
|
||||
int runelen(int c);
|
||||
|
||||
int isalpharune(Rune c);
|
||||
int islowerrune(Rune c);
|
||||
int isupperrune(Rune c);
|
||||
Rune tolowerrune(Rune c);
|
||||
Rune toupperrune(Rune c);
|
||||
const Rune* tolowerrune_full(Rune c);
|
||||
const Rune* toupperrune_full(Rune c);
|
||||
|
||||
#endif
|
2209
src/mujs/utfdata.h
Normal file
2209
src/mujs/utfdata.h
Normal file
File diff suppressed because it is too large
Load diff
7
src/openlibm/.github/dependabot.yml
vendored
Normal file
7
src/openlibm/.github/dependabot.yml
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "monthly"
|
93
src/openlibm/.github/workflows/ci.yml
vendored
Normal file
93
src/openlibm/.github/workflows/ci.yml
vendored
Normal file
|
@ -0,0 +1,93 @@
|
|||
name: CI
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags: '*'
|
||||
jobs:
|
||||
test-unix:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os:
|
||||
- ubuntu-latest
|
||||
- macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: make
|
||||
- run: make test
|
||||
windows:
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- { sys: mingw64, env: x86_64 }
|
||||
- { sys: mingw32, env: i686 }
|
||||
- { sys: ucrt64, env: ucrt-x86_64 } # Experimental!
|
||||
- { sys: clang64, env: clang-x86_64 } # Experimental!
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up the desired MSYS2 environment
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: ${{matrix.sys}}
|
||||
install: base-devel mingw-w64-${{matrix.env}}-toolchain
|
||||
- run: make
|
||||
- run: make test
|
||||
code-coverage-old:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup LCOV
|
||||
uses: hrishikesh-kadam/setup-lcov@v1
|
||||
- name: Build and Run tests
|
||||
run: make coverage -j
|
||||
# - name: Upload coverage to Codecov
|
||||
# uses: codecov/codecov-action@v5
|
||||
# with:
|
||||
# files: ./cov-html/libopenlibm.info
|
||||
# token: ${{ secrets.CODECOV_TOKEN }}
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: code-coverage-report-old
|
||||
path: ./cov-html/
|
||||
code-coverage:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Openlibm
|
||||
uses: actions/checkout@v4
|
||||
- name: Checkout Openlibm-test
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'JuliaMath/openlibm-test'
|
||||
path: 'openlibm-test'
|
||||
- name: Setup LCOV
|
||||
uses: hrishikesh-kadam/setup-lcov@v1
|
||||
- name: Build Openlibm
|
||||
run: make -j`nproc` CODE_COVERAGE=1
|
||||
- name: Run Test
|
||||
run: |
|
||||
make -j`nproc` -C openlibm-test \
|
||||
USE_OPENLIBM=1 OPENLIBM_HOME="$(pwd)" \
|
||||
SKIP_FP_EXCEPT_TEST=1 \
|
||||
- name: Show Test Result
|
||||
run: cat openlibm-test/src/REPORT
|
||||
- name: Gen Coverage Report
|
||||
run: make gen-cov-report
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
files: ./cov-html/libopenlibm.info
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: code-coverage-report
|
||||
path: ./cov-html/
|
54
src/openlibm/.github/workflows/cross-loongarch64.yml
vendored
Normal file
54
src/openlibm/.github/workflows/cross-loongarch64.yml
vendored
Normal file
|
@ -0,0 +1,54 @@
|
|||
# merge this file into cross.yml
|
||||
# when we can `sudo apt install gcc-loongarch64-linux-gnu` on ubuntu
|
||||
name: Cross
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags: '*'
|
||||
|
||||
jobs:
|
||||
build-cross-qemu:
|
||||
# TODO: We need Ubuntu 24.04 to use newer version of qemu,
|
||||
# switch to ubuntu-latest when `ubuntu-latest >= 24.04`
|
||||
runs-on: ubuntu-24.04
|
||||
name: build-cross-qemu-${{ matrix.config.arch }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
config:
|
||||
- { arch: loongarch64, triple: loongarch64-linux-gnu }
|
||||
env:
|
||||
ARCH: ${{ matrix.config.arch }}
|
||||
TRIPLE: ${{ matrix.config.triple }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install qemu
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -y qemu-user qemu-user-binfmt
|
||||
- name: Install gcc-${{ matrix.config.triple }}
|
||||
# package gcc-loongarch64-linux-gnu seems not exist
|
||||
# https://packages.debian.org/sid/amd64/gcc-loongarch64-linux-gnu
|
||||
run: sudo apt install -y gcc-14-loongarch64-linux-gnu
|
||||
- name: Build with ${{ matrix.config.triple }}-gcc
|
||||
run: |
|
||||
make ARCH=$ARCH TOOLPREFIX=$TRIPLE- \
|
||||
CC='loongarch64-linux-gnu-gcc-14' \
|
||||
AR='loongarch64-linux-gnu-gcc-ar-14' \
|
||||
- name: Build tests
|
||||
run: |
|
||||
make -C test ARCH=$ARCH TOOLPREFIX=$TRIPLE- \
|
||||
CC='loongarch64-linux-gnu-gcc-14' \
|
||||
AR='loongarch64-linux-gnu-gcc-ar-14' \
|
||||
- name: Run Tests
|
||||
env:
|
||||
QEMU_EXEC: qemu-${{ matrix.config.arch }}
|
||||
CROSS_LIB: /usr/${{ matrix.config.triple }}
|
||||
run: |
|
||||
$QEMU_EXEC -L . -L $CROSS_LIB/ test/test-float
|
||||
$QEMU_EXEC -L . -L $CROSS_LIB/ test/test-double
|
53
src/openlibm/.github/workflows/cross.yml
vendored
Normal file
53
src/openlibm/.github/workflows/cross.yml
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
name: Cross
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags: '*'
|
||||
|
||||
jobs:
|
||||
build-cross-qemu:
|
||||
# TODO: We need Ubuntu 24.04 to use newer version of qemu,
|
||||
# switch to ubuntu-latest when `ubuntu-latest >= 24.04`
|
||||
runs-on: ubuntu-24.04
|
||||
name: build-cross-qemu-${{ matrix.config.arch }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
config:
|
||||
- { arch: arm, triple: arm-linux-gnueabihf }
|
||||
- { arch: aarch64, triple: aarch64-linux-gnu }
|
||||
- { arch: ppc, triple: powerpc-linux-gnu }
|
||||
- { arch: ppc64, triple: powerpc64-linux-gnu }
|
||||
- { arch: ppc64le, triple: powerpc64le-linux-gnu }
|
||||
- { arch: mips, triple: mips-linux-gnu }
|
||||
- { arch: mipsel, triple: mipsel-linux-gnu }
|
||||
# Builds successfully, but tests fail.
|
||||
# - { arch: mips64, triple: mips64-linux-gnuabi64 }
|
||||
# - { arch: mips64el, triple: mips64el-linux-gnuabi64 }
|
||||
- { arch: riscv64, triple: riscv64-linux-gnu }
|
||||
- { arch: s390x, triple: s390x-linux-gnu }
|
||||
env:
|
||||
ARCH: ${{ matrix.config.arch }}
|
||||
TRIPLE: ${{ matrix.config.triple }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install qemu and toolchain gcc-${{ matrix.config.triple }}
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install qemu-user qemu-user-binfmt gcc-$TRIPLE -y
|
||||
- name: Build with ${{ matrix.config.triple }}-gcc
|
||||
run: make ARCH=$ARCH TOOLPREFIX=$TRIPLE-
|
||||
- name: Build tests
|
||||
run: make -C test ARCH=$ARCH TOOLPREFIX=$TRIPLE-
|
||||
- name: Run Tests
|
||||
env:
|
||||
QEMU_EXEC: qemu-${{ matrix.config.arch }}
|
||||
CROSS_LIB: /usr/${{ matrix.config.triple }}
|
||||
run: |
|
||||
$QEMU_EXEC -L . -L $CROSS_LIB/ test/test-float
|
||||
$QEMU_EXEC -L . -L $CROSS_LIB/ test/test-double
|
61
src/openlibm/.mailmap
Normal file
61
src/openlibm/.mailmap
Normal file
|
@ -0,0 +1,61 @@
|
|||
JuliaLang <julia-dev@googlegroups.com> <julia-dev@googlegroups.com>
|
||||
JuliaLang <julia-dev@googlegroups.com> <julia-math@googlegroups.com>
|
||||
|
||||
Jeff Bezanson <jeff.bezanson@gmail.com> <jeff.bezanson@gmail.com>
|
||||
Jeff Bezanson <jeff.bezanson@gmail.com> <bezanson@beagle.darwinproject.mit.edu>
|
||||
Jeff Bezanson <jeff.bezanson@gmail.com> <bezanson@caspian.caspian.mit.edu>
|
||||
Jeff Bezanson <jeff.bezanson@gmail.com> <bezanson@evolution.local>
|
||||
Jeff Bezanson <jeff.bezanson@gmail.com> <bezanson@mathstation045.mit.edu>
|
||||
Jeff Bezanson <jeff.bezanson@gmail.com> <bezanson@mathstation049.mit.edu>
|
||||
Jeff Bezanson <jeff.bezanson@gmail.com> <bezanson@mathstation186.mit.edu>
|
||||
Jeff Bezanson <jeff.bezanson@gmail.com> <bezanson@post.harvard.edu>
|
||||
Jeff Bezanson <jeff.bezanson@gmail.com> <bezanson@scooby-doo.csail.mit.edu>
|
||||
Jeff Bezanson <jeff.bezanson@gmail.com> <bezanson@shaggy.csail.mit.edu>
|
||||
Jeff Bezanson <jeff.bezanson@gmail.com> <jeff@lagann.(none)>
|
||||
Jeff Bezanson <jeff.bezanson@gmail.com> <julia@beowulf1.csail.mit.edu>
|
||||
Jeff Bezanson <jeff.bezanson@gmail.com> <vcloud@julia02.domain.local>
|
||||
|
||||
Stefan Karpinski <stefan@karpinski.org> <stefan@karpinski.org>
|
||||
Stefan Karpinski <stefan@karpinski.org> <stefan.karpinski@gmail.com>
|
||||
Stefan Karpinski <stefan@karpinski.org> <stefan.karpinski@post.harvard.edu>
|
||||
|
||||
Viral B. Shah <viral@mayin.org> <viral@mayin.org>
|
||||
Viral B. Shah <viral@mayin.org> <viral@beowulf1.csail.mit.edu>
|
||||
Viral B. Shah <viral@mayin.org> <viral@neumann.cs.ucsb.edu>
|
||||
Viral B. Shah <viral@mayin.org> <viral@ubuntu-VirtualBox.(none)>
|
||||
|
||||
George Xing <gxing@mit.edu> <gxing@mit.edu>
|
||||
George Xing <gxing@mit.edu> <noobiecubie@gmail.com>
|
||||
|
||||
Stephan Boyer <boyers@mit.edu> <boyers@mit.edu>
|
||||
Stephan Boyer <boyers@mit.edu> <stephan@julialang.xvm.mit.edu>
|
||||
Stephan Boyer <boyers@mit.edu> <stephan@ubuntu.(none)>
|
||||
Stephan Boyer <boyers@mit.edu> <stephan@ubuntu.ubuntu-domain>
|
||||
|
||||
Giuseppe Zingales <giuseppe.pet.zingales@gmail.com> <giuseppe.pet.zingales@gmail.com>
|
||||
Giuseppe Zingales <giuseppe.pet.zingales@gmail.com> <g3@ubuntu.ubuntu-domain>
|
||||
|
||||
Jameson Nash <jameson@mit.edu> <jameson@mit.edu>
|
||||
Jameson Nash <jameson@mit.edu> <vtjnash@comcast.net>
|
||||
Jameson Nash <jameson@mit.edu> <vtjnash@gmail.com>
|
||||
|
||||
Alan Edelman <mit.edelman@gmail.com> <mit.edelman@gmail.com>
|
||||
|
||||
PlayMyCode <joe@playmycode.com> <joe@playmycode.com>
|
||||
PlayMyCode <joe@playmycode.com> <hello@playmycode.com>
|
||||
|
||||
Corey M. Hoffstein <corey@hoffstein.com> <corey@hoffstein.com>
|
||||
Corey M. Hoffstein <corey@hoffstein.com> <corey@newfoundresearch.com>
|
||||
|
||||
Stefan Kroboth <stefan.kroboth@gmail.com> <stefan.kroboth@gmail.com>
|
||||
|
||||
Tim Holy <tim.holy@gmail.com> <tim.holy@gmail.com>
|
||||
Tim Holy <tim.holy@gmail.com> <holy@wustl.edu>
|
||||
|
||||
Patrick O'Leary <patrick.oleary@gmail.com> <patrick.oleary@gmail.com>
|
||||
|
||||
Ivan Mantova <horphus@gmail.com> <horphus@gmail.com>
|
||||
|
||||
Keno Fischer <kfischer@college.harvard.edu> <kfischer@college.harvard.edu>
|
||||
Keno Fischer <kfischer@college.harvard.edu> <kfischer+github@college.harvard.edu>
|
||||
Keno Fischer <kfischer@college.harvard.edu> <kenof@stanford.edu>
|
576
src/openlibm/CMakeLists.txt
Executable file
576
src/openlibm/CMakeLists.txt
Executable file
|
@ -0,0 +1,576 @@
|
|||
cmake_minimum_required(VERSION 3.25)
|
||||
|
||||
# Get version string from Make.inc
|
||||
file(READ "${CMAKE_CURRENT_LIST_DIR}/Make.inc" MAKE_FILE)
|
||||
string(REGEX MATCH "VERSION = ([0-9\.]+)" _ ${MAKE_FILE})
|
||||
|
||||
project(openlibm
|
||||
VERSION ${CMAKE_MATCH_1}
|
||||
LANGUAGES C ASM)
|
||||
|
||||
option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
|
||||
|
||||
add_library("${PROJECT_NAME}")
|
||||
|
||||
# Find the relevant folder depending on the architecture
|
||||
set(OPENLIBM_ARCH_FOLDER ${CMAKE_SYSTEM_PROCESSOR})
|
||||
string(TOLOWER "${OPENLIBM_ARCH_FOLDER}" OPENLIBM_ARCH_FOLDER)
|
||||
|
||||
if(${OPENLIBM_ARCH_FOLDER} STREQUAL "amd64" OR ${OPENLIBM_ARCH_FOLDER} STREQUAL "x86_64")
|
||||
set(OPENLIBM_ARCH_FOLDER "amd64")
|
||||
elseif(${OPENLIBM_ARCH_FOLDER} STREQUAL "arm64" OR ${OPENLIBM_ARCH_FOLDER} STREQUAL "aarch64")
|
||||
set(OPENLIBM_ARCH_FOLDER "aarch64")
|
||||
elseif(${OPENLIBM_ARCH_FOLDER} STREQUAL "armv7-a")
|
||||
set(OPENLIBM_ARCH_FOLDER "arm")
|
||||
elseif(${OPENLIBM_ARCH_FOLDER} STREQUAL "x86" OR ${OPENLIBM_ARCH_FOLDER} STREQUAL "i686")
|
||||
set(OPENLIBM_ARCH_FOLDER "i387")
|
||||
elseif(${OPENLIBM_ARCH_FOLDER} STREQUAL "powerpc")
|
||||
set(OPENLIBM_ARCH_FOLDER "powerpc")
|
||||
elseif(${OPENLIBM_ARCH_FOLDER} STREQUAL "riscv64")
|
||||
set(OPENLIBM_ARCH_FOLDER "riscv64")
|
||||
else()
|
||||
message(FATAL_ERROR "${PROJECT_NAME} not set up for detected architecture: ${OPENLIBM_ARCH_FOLDER}")
|
||||
endif()
|
||||
|
||||
|
||||
# Compile flags
|
||||
list(APPEND C_ASM_COMPILE_FLAGS "-ffp-contract=off" "-fno-fast-math" "-fno-rounding-math" "-fno-math-errno")
|
||||
list(APPEND C_ASM_COMPILE_FLAGS "-fPIC" "-std=c99" "-fno-builtin")
|
||||
list(APPEND C_ASM_COMPILE_FLAGS "-Wall" "-Wno-implicit-function-declaration")
|
||||
list(APPEND C_ASM_COMPILE_FLAGS "-DASSEMBLER" "-D__BSD_VISIBLE" "-O3")
|
||||
|
||||
# Compiler-specific compile flags
|
||||
if("${CMAKE_C_COMPILER_ID}" MATCHES "Clang")
|
||||
list(APPEND C_ASM_COMPILE_FLAGS "-fno-strict-aliasing" "-ffp-exception-behavior=strict")
|
||||
elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
|
||||
list(APPEND C_ASM_COMPILE_FLAGS "-fno-gnu89-inline")
|
||||
else()
|
||||
message(FATAL_ERROR "${PROJECT_NAME} not set up to be compiled with ${CMAKE_C_COMPILER_ID}")
|
||||
endif()
|
||||
|
||||
# Architecture-specific compile flags - take advantage of sse on x86
|
||||
if(${OPENLIBM_ARCH_FOLDER} STREQUAL "i387")
|
||||
list(APPEND C_ASM_COMPILE_FLAGS "-march=i686" "-m32" "-msse2" "-mfpmath=sse")
|
||||
elseif(${OPENLIBM_ARCH_FOLDER} STREQUAL "amd64")
|
||||
list(APPEND C_ASM_COMPILE_FLAGS "-m64" "-msse2" "-mfpmath=sse")
|
||||
endif()
|
||||
|
||||
# Suppress warnings if requested
|
||||
if(OPENLIBM_SUPPRESS_WARNINGS)
|
||||
list(APPEND C_ASM_COMPILE_FLAGS "-w")
|
||||
endif()
|
||||
|
||||
# Add compile flags
|
||||
target_compile_options("${PROJECT_NAME}" PUBLIC ${C_ASM_COMPILE_FLAGS})
|
||||
|
||||
# Project Source
|
||||
set(PROJECT_SRC "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
|
||||
# Common
|
||||
list(APPEND OPENLIBM_C_SOURCE
|
||||
# src
|
||||
"${PROJECT_SRC}/src/common.c"
|
||||
"${PROJECT_SRC}/src/e_acos.c"
|
||||
"${PROJECT_SRC}/src/e_acosf.c"
|
||||
"${PROJECT_SRC}/src/e_acosh.c"
|
||||
"${PROJECT_SRC}/src/e_acoshf.c"
|
||||
"${PROJECT_SRC}/src/e_asin.c"
|
||||
"${PROJECT_SRC}/src/e_asinf.c"
|
||||
"${PROJECT_SRC}/src/e_atan2.c"
|
||||
"${PROJECT_SRC}/src/e_atan2f.c"
|
||||
"${PROJECT_SRC}/src/e_atanh.c"
|
||||
"${PROJECT_SRC}/src/e_atanhf.c"
|
||||
"${PROJECT_SRC}/src/e_cosh.c"
|
||||
"${PROJECT_SRC}/src/e_coshf.c"
|
||||
"${PROJECT_SRC}/src/e_exp.c"
|
||||
"${PROJECT_SRC}/src/e_expf.c"
|
||||
"${PROJECT_SRC}/src/e_fmod.c"
|
||||
"${PROJECT_SRC}/src/e_fmodf.c"
|
||||
"${PROJECT_SRC}/src/e_hypot.c"
|
||||
"${PROJECT_SRC}/src/e_hypotf.c"
|
||||
"${PROJECT_SRC}/src/e_j0.c"
|
||||
"${PROJECT_SRC}/src/e_j0f.c"
|
||||
"${PROJECT_SRC}/src/e_j1.c"
|
||||
"${PROJECT_SRC}/src/e_j1f.c"
|
||||
"${PROJECT_SRC}/src/e_jn.c"
|
||||
"${PROJECT_SRC}/src/e_jnf.c"
|
||||
"${PROJECT_SRC}/src/e_lgamma.c"
|
||||
"${PROJECT_SRC}/src/e_lgamma_r.c"
|
||||
"${PROJECT_SRC}/src/e_lgammaf.c"
|
||||
"${PROJECT_SRC}/src/e_lgammaf_r.c"
|
||||
"${PROJECT_SRC}/src/e_log.c"
|
||||
"${PROJECT_SRC}/src/e_log10.c"
|
||||
"${PROJECT_SRC}/src/e_log10f.c"
|
||||
"${PROJECT_SRC}/src/e_log2.c"
|
||||
"${PROJECT_SRC}/src/e_log2f.c"
|
||||
"${PROJECT_SRC}/src/e_logf.c"
|
||||
"${PROJECT_SRC}/src/e_pow.c"
|
||||
"${PROJECT_SRC}/src/e_powf.c"
|
||||
"${PROJECT_SRC}/src/e_remainder.c"
|
||||
"${PROJECT_SRC}/src/e_remainderf.c"
|
||||
"${PROJECT_SRC}/src/e_rem_pio2.c"
|
||||
"${PROJECT_SRC}/src/e_rem_pio2f.c"
|
||||
"${PROJECT_SRC}/src/e_sinh.c"
|
||||
"${PROJECT_SRC}/src/e_sinhf.c"
|
||||
"${PROJECT_SRC}/src/e_sqrt.c"
|
||||
"${PROJECT_SRC}/src/e_sqrtf.c"
|
||||
"${PROJECT_SRC}/src/k_cos.c"
|
||||
"${PROJECT_SRC}/src/k_exp.c"
|
||||
"${PROJECT_SRC}/src/k_expf.c"
|
||||
"${PROJECT_SRC}/src/k_rem_pio2.c"
|
||||
"${PROJECT_SRC}/src/k_sin.c"
|
||||
"${PROJECT_SRC}/src/k_tan.c"
|
||||
"${PROJECT_SRC}/src/k_cosf.c"
|
||||
"${PROJECT_SRC}/src/k_sinf.c"
|
||||
"${PROJECT_SRC}/src/k_tanf.c"
|
||||
"${PROJECT_SRC}/src/s_asinh.c"
|
||||
"${PROJECT_SRC}/src/s_asinhf.c"
|
||||
"${PROJECT_SRC}/src/s_atan.c"
|
||||
"${PROJECT_SRC}/src/s_atanf.c"
|
||||
"${PROJECT_SRC}/src/s_carg.c"
|
||||
"${PROJECT_SRC}/src/s_cargf.c"
|
||||
"${PROJECT_SRC}/src/s_cbrt.c"
|
||||
"${PROJECT_SRC}/src/s_cbrtf.c"
|
||||
"${PROJECT_SRC}/src/s_ceil.c"
|
||||
"${PROJECT_SRC}/src/s_ceilf.c"
|
||||
"${PROJECT_SRC}/src/s_copysign.c"
|
||||
"${PROJECT_SRC}/src/s_copysignf.c"
|
||||
"${PROJECT_SRC}/src/s_cos.c"
|
||||
"${PROJECT_SRC}/src/s_cosf.c"
|
||||
"${PROJECT_SRC}/src/s_csqrt.c"
|
||||
"${PROJECT_SRC}/src/s_csqrtf.c"
|
||||
"${PROJECT_SRC}/src/s_erf.c"
|
||||
"${PROJECT_SRC}/src/s_erff.c"
|
||||
"${PROJECT_SRC}/src/s_exp2.c"
|
||||
"${PROJECT_SRC}/src/s_exp2f.c"
|
||||
"${PROJECT_SRC}/src/s_expm1.c"
|
||||
"${PROJECT_SRC}/src/s_expm1f.c"
|
||||
"${PROJECT_SRC}/src/s_fabs.c"
|
||||
"${PROJECT_SRC}/src/s_fabsf.c"
|
||||
"${PROJECT_SRC}/src/s_fdim.c"
|
||||
"${PROJECT_SRC}/src/s_floor.c"
|
||||
"${PROJECT_SRC}/src/s_floorf.c"
|
||||
"${PROJECT_SRC}/src/s_fmax.c"
|
||||
"${PROJECT_SRC}/src/s_fmaxf.c"
|
||||
"${PROJECT_SRC}/src/s_fmin.c"
|
||||
"${PROJECT_SRC}/src/s_fminf.c"
|
||||
"${PROJECT_SRC}/src/s_fpclassify.c"
|
||||
"${PROJECT_SRC}/src/s_frexp.c"
|
||||
"${PROJECT_SRC}/src/s_frexpf.c"
|
||||
"${PROJECT_SRC}/src/s_ilogb.c"
|
||||
"${PROJECT_SRC}/src/s_ilogbf.c"
|
||||
"${PROJECT_SRC}/src/s_isinf.c"
|
||||
"${PROJECT_SRC}/src/s_isfinite.c"
|
||||
"${PROJECT_SRC}/src/s_isnormal.c"
|
||||
"${PROJECT_SRC}/src/s_isnan.c"
|
||||
"${PROJECT_SRC}/src/s_log1p.c"
|
||||
"${PROJECT_SRC}/src/s_log1pf.c"
|
||||
"${PROJECT_SRC}/src/s_logb.c"
|
||||
"${PROJECT_SRC}/src/s_logbf.c"
|
||||
"${PROJECT_SRC}/src/s_modf.c"
|
||||
"${PROJECT_SRC}/src/s_modff.c"
|
||||
"${PROJECT_SRC}/src/s_nextafter.c"
|
||||
"${PROJECT_SRC}/src/s_nextafterf.c"
|
||||
"${PROJECT_SRC}/src/s_nexttowardf.c"
|
||||
"${PROJECT_SRC}/src/s_remquo.c"
|
||||
"${PROJECT_SRC}/src/s_remquof.c"
|
||||
"${PROJECT_SRC}/src/s_rint.c"
|
||||
"${PROJECT_SRC}/src/s_rintf.c"
|
||||
"${PROJECT_SRC}/src/s_round.c"
|
||||
"${PROJECT_SRC}/src/s_roundf.c"
|
||||
"${PROJECT_SRC}/src/s_scalbln.c"
|
||||
"${PROJECT_SRC}/src/s_scalbn.c"
|
||||
"${PROJECT_SRC}/src/s_scalbnf.c"
|
||||
"${PROJECT_SRC}/src/s_signbit.c"
|
||||
"${PROJECT_SRC}/src/s_signgam.c"
|
||||
"${PROJECT_SRC}/src/s_sin.c"
|
||||
"${PROJECT_SRC}/src/s_sincos.c"
|
||||
"${PROJECT_SRC}/src/s_sinf.c"
|
||||
"${PROJECT_SRC}/src/s_sincosf.c"
|
||||
"${PROJECT_SRC}/src/s_tan.c"
|
||||
"${PROJECT_SRC}/src/s_tanf.c"
|
||||
"${PROJECT_SRC}/src/s_tanh.c"
|
||||
"${PROJECT_SRC}/src/s_tanhf.c"
|
||||
"${PROJECT_SRC}/src/s_tgammaf.c"
|
||||
"${PROJECT_SRC}/src/s_trunc.c"
|
||||
"${PROJECT_SRC}/src/s_truncf.c"
|
||||
"${PROJECT_SRC}/src/s_cpow.c"
|
||||
"${PROJECT_SRC}/src/s_cpowf.c"
|
||||
"${PROJECT_SRC}/src/w_cabs.c"
|
||||
"${PROJECT_SRC}/src/w_cabsf.c"
|
||||
|
||||
"${PROJECT_SRC}/src/s_fma.c"
|
||||
"${PROJECT_SRC}/src/s_fmaf.c"
|
||||
"${PROJECT_SRC}/src/s_lrint.c"
|
||||
"${PROJECT_SRC}/src/s_lrintf.c"
|
||||
"${PROJECT_SRC}/src/s_lround.c"
|
||||
"${PROJECT_SRC}/src/s_lroundf.c"
|
||||
"${PROJECT_SRC}/src/s_llrint.c"
|
||||
"${PROJECT_SRC}/src/s_llrintf.c"
|
||||
"${PROJECT_SRC}/src/s_llround.c"
|
||||
"${PROJECT_SRC}/src/s_llroundf.c"
|
||||
"${PROJECT_SRC}/src/s_nearbyint.c"
|
||||
|
||||
# C99 complex functions
|
||||
"${PROJECT_SRC}/src/s_ccosh.c"
|
||||
"${PROJECT_SRC}/src/s_ccoshf.c"
|
||||
"${PROJECT_SRC}/src/s_cexp.c"
|
||||
"${PROJECT_SRC}/src/s_cexpf.c"
|
||||
"${PROJECT_SRC}/src/s_cimag.c"
|
||||
"${PROJECT_SRC}/src/s_cimagf.c"
|
||||
"${PROJECT_SRC}/src/s_conj.c"
|
||||
"${PROJECT_SRC}/src/s_conjf.c"
|
||||
"${PROJECT_SRC}/src/s_cproj.c"
|
||||
"${PROJECT_SRC}/src/s_cprojf.c"
|
||||
"${PROJECT_SRC}/src/s_creal.c"
|
||||
"${PROJECT_SRC}/src/s_crealf.c"
|
||||
"${PROJECT_SRC}/src/s_csinh.c"
|
||||
"${PROJECT_SRC}/src/s_csinhf.c"
|
||||
"${PROJECT_SRC}/src/s_ctanh.c"
|
||||
"${PROJECT_SRC}/src/s_ctanhf.c"
|
||||
"${PROJECT_SRC}/src/s_cacos.c"
|
||||
"${PROJECT_SRC}/src/s_cacosf.c"
|
||||
"${PROJECT_SRC}/src/s_cacosh.c"
|
||||
"${PROJECT_SRC}/src/s_cacoshf.c"
|
||||
"${PROJECT_SRC}/src/s_casin.c"
|
||||
"${PROJECT_SRC}/src/s_casinf.c"
|
||||
"${PROJECT_SRC}/src/s_casinh.c"
|
||||
"${PROJECT_SRC}/src/s_casinhf.c"
|
||||
"${PROJECT_SRC}/src/s_catan.c"
|
||||
"${PROJECT_SRC}/src/s_catanf.c"
|
||||
"${PROJECT_SRC}/src/s_catanh.c"
|
||||
"${PROJECT_SRC}/src/s_catanhf.c"
|
||||
"${PROJECT_SRC}/src/s_clog.c"
|
||||
"${PROJECT_SRC}/src/s_clogf.c"
|
||||
|
||||
# bsdsrc
|
||||
"${PROJECT_SRC}/bsdsrc/b_exp.c"
|
||||
"${PROJECT_SRC}/bsdsrc/b_log.c"
|
||||
"${PROJECT_SRC}/bsdsrc/b_tgamma.c"
|
||||
)
|
||||
|
||||
if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
|
||||
list(APPEND OPENLIBM_C_SOURCE
|
||||
"${PROJECT_SRC}/src/s_nan.c"
|
||||
)
|
||||
endif()
|
||||
|
||||
# Determine if long double and double are the same size
|
||||
include(CheckCSourceCompiles)
|
||||
check_c_source_compiles("
|
||||
#include <float.h>
|
||||
#if (LDBL_MANT_DIG == DBL_MANT_DIG)
|
||||
#error \"long double and double are the same size\"
|
||||
#endif
|
||||
int main(void ) { return 0; }
|
||||
" LONG_DOUBLE_NOT_DOUBLE)
|
||||
|
||||
# Add in long double functions for x86, x64 and aarch64
|
||||
if(LONG_DOUBLE_NOT_DOUBLE)
|
||||
list(APPEND OPENLIBM_C_SOURCE
|
||||
"${PROJECT_SRC}/src/s_copysignl.c"
|
||||
"${PROJECT_SRC}/src/s_fabsl.c"
|
||||
"${PROJECT_SRC}/src/s_llrintl.c"
|
||||
"${PROJECT_SRC}/src/s_lrintl.c"
|
||||
"${PROJECT_SRC}/src/s_modfl.c"
|
||||
|
||||
"${PROJECT_SRC}/src/e_acosl.c"
|
||||
"${PROJECT_SRC}/src/e_asinl.c"
|
||||
"${PROJECT_SRC}/src/e_atan2l.c"
|
||||
"${PROJECT_SRC}/src/e_fmodl.c"
|
||||
"${PROJECT_SRC}/src/s_fmaxl.c"
|
||||
"${PROJECT_SRC}/src/s_fminl.c"
|
||||
"${PROJECT_SRC}/src/s_ilogbl.c"
|
||||
"${PROJECT_SRC}/src/e_hypotl.c"
|
||||
"${PROJECT_SRC}/src/e_lgammal.c"
|
||||
"${PROJECT_SRC}/src/e_remainderl.c"
|
||||
"${PROJECT_SRC}/src/e_sqrtl.c"
|
||||
"${PROJECT_SRC}/src/s_atanl.c"
|
||||
"${PROJECT_SRC}/src/s_ceill.c"
|
||||
"${PROJECT_SRC}/src/s_cosl.c"
|
||||
"${PROJECT_SRC}/src/s_cprojl.c"
|
||||
"${PROJECT_SRC}/src/s_csqrtl.c"
|
||||
"${PROJECT_SRC}/src/s_floorl.c"
|
||||
"${PROJECT_SRC}/src/s_fmal.c"
|
||||
"${PROJECT_SRC}/src/s_frexpl.c"
|
||||
"${PROJECT_SRC}/src/s_logbl.c"
|
||||
"${PROJECT_SRC}/src/s_nexttoward.c"
|
||||
"${PROJECT_SRC}/src/s_remquol.c"
|
||||
"${PROJECT_SRC}/src/s_roundl.c"
|
||||
"${PROJECT_SRC}/src/s_lroundl.c"
|
||||
"${PROJECT_SRC}/src/s_llroundl.c"
|
||||
"${PROJECT_SRC}/src/s_cpowl.c"
|
||||
"${PROJECT_SRC}/src/s_cargl.c"
|
||||
"${PROJECT_SRC}/src/s_sinl.c"
|
||||
"${PROJECT_SRC}/src/s_sincosl.c"
|
||||
"${PROJECT_SRC}/src/s_tanl.c"
|
||||
"${PROJECT_SRC}/src/s_truncl.c"
|
||||
"${PROJECT_SRC}/src/w_cabsl.c"
|
||||
"${PROJECT_SRC}/src/s_nextafterl.c"
|
||||
"${PROJECT_SRC}/src/s_rintl.c"
|
||||
"${PROJECT_SRC}/src/s_scalbnl.c"
|
||||
"${PROJECT_SRC}/src/polevll.c"
|
||||
"${PROJECT_SRC}/src/s_casinl.c"
|
||||
"${PROJECT_SRC}/src/s_ctanl.c"
|
||||
"${PROJECT_SRC}/src/s_cimagl.c"
|
||||
"${PROJECT_SRC}/src/s_conjl.c"
|
||||
"${PROJECT_SRC}/src/s_creall.c"
|
||||
"${PROJECT_SRC}/src/s_cacoshl.c"
|
||||
"${PROJECT_SRC}/src/s_catanhl.c"
|
||||
"${PROJECT_SRC}/src/s_casinhl.c"
|
||||
"${PROJECT_SRC}/src/s_catanl.c"
|
||||
"${PROJECT_SRC}/src/s_csinl.c"
|
||||
"${PROJECT_SRC}/src/s_cacosl.c"
|
||||
"${PROJECT_SRC}/src/s_cexpl.c"
|
||||
"${PROJECT_SRC}/src/s_csinhl.c"
|
||||
"${PROJECT_SRC}/src/s_ccoshl.c"
|
||||
"${PROJECT_SRC}/src/s_clogl.c"
|
||||
"${PROJECT_SRC}/src/s_ctanhl.c"
|
||||
"${PROJECT_SRC}/src/s_ccosl.c"
|
||||
"${PROJECT_SRC}/src/s_cbrtl.c"
|
||||
)
|
||||
endif()
|
||||
|
||||
if (LONG_DOUBLE_NOT_DOUBLE)
|
||||
if (${OPENLIBM_ARCH_FOLDER} STREQUAL "i387" OR ${OPENLIBM_ARCH_FOLDER} STREQUAL "amd64")
|
||||
list(APPEND OPENLIBM_C_SOURCE
|
||||
# ld80
|
||||
"${PROJECT_SRC}/ld80/invtrig.c"
|
||||
"${PROJECT_SRC}/ld80/e_acoshl.c"
|
||||
"${PROJECT_SRC}/ld80/e_powl.c"
|
||||
"${PROJECT_SRC}/ld80/k_tanl.c"
|
||||
"${PROJECT_SRC}/ld80/s_exp2l.c"
|
||||
"${PROJECT_SRC}/ld80/e_atanhl.c"
|
||||
"${PROJECT_SRC}/ld80/e_lgammal_r.c"
|
||||
"${PROJECT_SRC}/ld80/e_sinhl.c"
|
||||
"${PROJECT_SRC}/ld80/s_asinhl.c"
|
||||
"${PROJECT_SRC}/ld80/s_expm1l.c"
|
||||
"${PROJECT_SRC}/ld80/e_coshl.c"
|
||||
"${PROJECT_SRC}/ld80/e_log10l.c"
|
||||
"${PROJECT_SRC}/ld80/e_tgammal.c"
|
||||
"${PROJECT_SRC}/ld80/e_expl.c"
|
||||
"${PROJECT_SRC}/ld80/e_log2l.c"
|
||||
"${PROJECT_SRC}/ld80/k_cosl.c"
|
||||
"${PROJECT_SRC}/ld80/s_log1pl.c"
|
||||
"${PROJECT_SRC}/ld80/s_tanhl.c"
|
||||
"${PROJECT_SRC}/ld80/e_logl.c"
|
||||
"${PROJECT_SRC}/ld80/k_sinl.c"
|
||||
"${PROJECT_SRC}/ld80/s_erfl.c"
|
||||
)
|
||||
|
||||
if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
|
||||
list(APPEND OPENLIBM_C_SOURCE
|
||||
"${PROJECT_SRC}/ld80/s_nanl.c"
|
||||
)
|
||||
endif()
|
||||
else()
|
||||
if(${OPENLIBM_ARCH_FOLDER} STREQUAL "aarch64")
|
||||
list(APPEND OPENLIBM_C_SOURCE
|
||||
# ld128
|
||||
"${PROJECT_SRC}/ld128/invtrig.c"
|
||||
"${PROJECT_SRC}/ld128/e_acoshl.c"
|
||||
"${PROJECT_SRC}/ld128/e_powl.c"
|
||||
"${PROJECT_SRC}/ld128/k_tanl.c"
|
||||
"${PROJECT_SRC}/ld128/s_exp2l.c"
|
||||
"${PROJECT_SRC}/ld128/e_atanhl.c"
|
||||
"${PROJECT_SRC}/ld128/e_lgammal_r.c"
|
||||
"${PROJECT_SRC}/ld128/e_sinhl.c"
|
||||
"${PROJECT_SRC}/ld128/s_asinhl.c"
|
||||
"${PROJECT_SRC}/ld128/s_expm1l.c"
|
||||
"${PROJECT_SRC}/ld128/e_coshl.c"
|
||||
"${PROJECT_SRC}/ld128/e_log10l.c"
|
||||
"${PROJECT_SRC}/ld128/e_tgammal.c"
|
||||
"${PROJECT_SRC}/ld128/e_expl.c"
|
||||
"${PROJECT_SRC}/ld128/e_log2l.c"
|
||||
"${PROJECT_SRC}/ld128/k_cosl.c"
|
||||
"${PROJECT_SRC}/ld128/s_log1pl.c"
|
||||
"${PROJECT_SRC}/ld128/s_tanhl.c"
|
||||
"${PROJECT_SRC}/ld128/e_logl.c"
|
||||
"${PROJECT_SRC}/ld128/k_sinl.c"
|
||||
"${PROJECT_SRC}/ld128/s_erfl.c"
|
||||
)
|
||||
|
||||
if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
|
||||
list(APPEND OPENLIBM_C_SOURCE
|
||||
"${PROJECT_SRC}/ld128/s_nanl.c"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Architecture-specific sources
|
||||
if (${OPENLIBM_ARCH_FOLDER} STREQUAL "amd64")
|
||||
list(APPEND OPENLIBM_C_SOURCE
|
||||
"${PROJECT_SRC}/amd64/fenv.c"
|
||||
)
|
||||
|
||||
list(APPEND OPENLIBM_ASM_SOURCE
|
||||
"${PROJECT_SRC}/amd64/e_remainder.S"
|
||||
"${PROJECT_SRC}/amd64/e_remainderf.S"
|
||||
"${PROJECT_SRC}/amd64/e_remainderl.S"
|
||||
"${PROJECT_SRC}/amd64/e_sqrt.S"
|
||||
"${PROJECT_SRC}/amd64/e_sqrtf.S"
|
||||
"${PROJECT_SRC}/amd64/e_sqrtl.S"
|
||||
"${PROJECT_SRC}/amd64/s_llrint.S"
|
||||
"${PROJECT_SRC}/amd64/s_llrintf.S"
|
||||
"${PROJECT_SRC}/amd64/s_llrintl.S"
|
||||
"${PROJECT_SRC}/amd64/s_logbl.S"
|
||||
"${PROJECT_SRC}/amd64/s_lrint.S"
|
||||
"${PROJECT_SRC}/amd64/s_lrintf.S"
|
||||
"${PROJECT_SRC}/amd64/s_lrintl.S"
|
||||
"${PROJECT_SRC}/amd64/s_remquo.S"
|
||||
"${PROJECT_SRC}/amd64/s_remquof.S"
|
||||
"${PROJECT_SRC}/amd64/s_remquol.S"
|
||||
"${PROJECT_SRC}/amd64/s_rintl.S"
|
||||
"${PROJECT_SRC}/amd64/s_scalbn.S"
|
||||
"${PROJECT_SRC}/amd64/s_scalbnf.S"
|
||||
"${PROJECT_SRC}/amd64/s_scalbnl.S"
|
||||
"${PROJECT_SRC}/amd64/e_fmod.S"
|
||||
"${PROJECT_SRC}/amd64/e_fmodf.S"
|
||||
"${PROJECT_SRC}/amd64/e_fmodl.S"
|
||||
)
|
||||
|
||||
elseif(${OPENLIBM_ARCH_FOLDER} STREQUAL "aarch64")
|
||||
list(APPEND OPENLIBM_C_SOURCE
|
||||
"${PROJECT_SRC}/aarch64/fenv.c"
|
||||
)
|
||||
|
||||
elseif(${OPENLIBM_ARCH_FOLDER} STREQUAL "arm")
|
||||
list(APPEND OPENLIBM_C_SOURCE
|
||||
"${PROJECT_SRC}/${OPENLIBM_ARCH_FOLDER}/fenv.c"
|
||||
)
|
||||
|
||||
elseif(${OPENLIBM_ARCH_FOLDER} STREQUAL "i387")
|
||||
list(APPEND OPENLIBM_C_SOURCE
|
||||
"${PROJECT_SRC}/i387/fenv.c"
|
||||
)
|
||||
|
||||
list(APPEND OPENLIBM_ASM_SOURCE
|
||||
"${PROJECT_SRC}/i387/e_exp.S"
|
||||
"${PROJECT_SRC}/i387/e_fmod.S"
|
||||
"${PROJECT_SRC}/i387/e_log.S"
|
||||
"${PROJECT_SRC}/i387/e_log10.S"
|
||||
"${PROJECT_SRC}/i387/e_remainder.S"
|
||||
"${PROJECT_SRC}/i387/e_sqrt.S"
|
||||
"${PROJECT_SRC}/i387/s_ceil.S"
|
||||
"${PROJECT_SRC}/i387/s_copysign.S"
|
||||
"${PROJECT_SRC}/i387/s_floor.S"
|
||||
"${PROJECT_SRC}/i387/s_llrint.S"
|
||||
"${PROJECT_SRC}/i387/s_logb.S"
|
||||
"${PROJECT_SRC}/i387/s_lrint.S"
|
||||
"${PROJECT_SRC}/i387/s_remquo.S"
|
||||
"${PROJECT_SRC}/i387/s_rint.S"
|
||||
"${PROJECT_SRC}/i387/s_tan.S"
|
||||
"${PROJECT_SRC}/i387/s_trunc.S"
|
||||
|
||||
# float counterparts
|
||||
"${PROJECT_SRC}/i387/e_log10f.S"
|
||||
"${PROJECT_SRC}/i387/e_logf.S"
|
||||
"${PROJECT_SRC}/i387/e_remainderf.S"
|
||||
"${PROJECT_SRC}/i387/e_sqrtf.S"
|
||||
"${PROJECT_SRC}/i387/s_ceilf.S"
|
||||
"${PROJECT_SRC}/i387/s_copysignf.S"
|
||||
"${PROJECT_SRC}/i387/s_floorf.S"
|
||||
"${PROJECT_SRC}/i387/s_llrintf.S"
|
||||
"${PROJECT_SRC}/i387/s_logbf.S"
|
||||
"${PROJECT_SRC}/i387/s_lrintf.S"
|
||||
"${PROJECT_SRC}/i387/s_remquof.S"
|
||||
"${PROJECT_SRC}/i387/s_rintf.S"
|
||||
"${PROJECT_SRC}/i387/s_truncf.S"
|
||||
|
||||
# long double counterparts
|
||||
"${PROJECT_SRC}/i387/e_remainderl.S"
|
||||
"${PROJECT_SRC}/i387/e_sqrtl.S"
|
||||
"${PROJECT_SRC}/i387/s_ceill.S"
|
||||
"${PROJECT_SRC}/i387/s_copysignl.S"
|
||||
"${PROJECT_SRC}/i387/s_floorl.S"
|
||||
"${PROJECT_SRC}/i387/s_llrintl.S"
|
||||
"${PROJECT_SRC}/i387/s_logbl.S"
|
||||
"${PROJECT_SRC}/i387/s_lrintl.S"
|
||||
"${PROJECT_SRC}/i387/s_remquol.S"
|
||||
"${PROJECT_SRC}/i387/s_rintl.S"
|
||||
"${PROJECT_SRC}/i387/s_truncl.S"
|
||||
)
|
||||
|
||||
if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
|
||||
list(APPEND OPENLIBM_ASM_SOURCE
|
||||
"${PROJECT_SRC}/i387/s_scalbn.S"
|
||||
"${PROJECT_SRC}/i387/s_scalbnf.S"
|
||||
"${PROJECT_SRC}/i387/s_scalbnl.S"
|
||||
)
|
||||
endif()
|
||||
|
||||
elseif(${OPENLIBM_ARCH_FOLDER} STREQUAL "powerpc")
|
||||
list(APPEND OPENLIBM_C_SOURCE
|
||||
"${PROJECT_SRC}/powerpc/fenv.c"
|
||||
)
|
||||
elseif(${OPENLIBM_ARCH_FOLDER} STREQUAL "riscv64")
|
||||
list(APPEND OPENLIBM_C_SOURCE
|
||||
"${PROJECT_SRC}/riscv64/fenv.c")
|
||||
else()
|
||||
message(FATAL_ERROR "${PROJECT_NAME} CMake build is not set up for ${OPENLIBM_ARCH_FOLDER}")
|
||||
endif()
|
||||
|
||||
|
||||
# Filter out C implementation from compilation list if a native implementation exists
|
||||
foreach(FILE_TO_REMOVE ${OPENLIBM_ASM_SOURCE})
|
||||
# Get filename and strip out extension
|
||||
cmake_path(GET FILE_TO_REMOVE FILENAME FILENAME_TO_REMOVE)
|
||||
cmake_path(REMOVE_EXTENSION FILENAME_TO_REMOVE OUTPUT_VARIABLE FILENAME_TO_REMOVE)
|
||||
message(DEBUG "Filename to remove: ${FILENAME_TO_REMOVE}")
|
||||
|
||||
# Go through files and remove one with the same name
|
||||
foreach(CUR_FILE ${OPENLIBM_C_SOURCE})
|
||||
cmake_path(GET CUR_FILE FILENAME CUR_FILENAME)
|
||||
cmake_path(REMOVE_EXTENSION CUR_FILENAME OUTPUT_VARIABLE CUR_FILENAME)
|
||||
|
||||
if(${CUR_FILENAME} STREQUAL ${FILENAME_TO_REMOVE})
|
||||
list(REMOVE_ITEM OPENLIBM_C_SOURCE ${CUR_FILE})
|
||||
message(DEBUG "Removed source file from compilation list: ${CUR_FILE}")
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
|
||||
# Add sources
|
||||
target_sources("${PROJECT_NAME}" PRIVATE ${OPENLIBM_C_SOURCE}
|
||||
${OPENLIBM_ASM_SOURCE}
|
||||
)
|
||||
|
||||
|
||||
# Include directories
|
||||
list(APPEND OPENLIBM_INCLUDE_DIRS
|
||||
"${PROJECT_SRC}"
|
||||
"${PROJECT_SRC}/include"
|
||||
"${PROJECT_SRC}/${OPENLIBM_ARCH_FOLDER}"
|
||||
"${PROJECT_SRC}/src"
|
||||
)
|
||||
|
||||
if(${OPENLIBM_ARCH_FOLDER} STREQUAL "i387" OR ${OPENLIBM_ARCH_FOLDER} STREQUAL "amd64" OR ${OPENLIBM_ARCH_FOLDER} STREQUAL "powerpc")
|
||||
list(APPEND OPENLIBM_INCLUDE_DIRS "${PROJECT_SRC}/ld80")
|
||||
else()
|
||||
if(${OPENLIBM_ARCH_FOLDER} STREQUAL "aarch64" OR ${OPENLIBM_ARCH_FOLDER} STREQUAL "riscv64")
|
||||
list(APPEND OPENLIBM_INCLUDE_DIRS "${PROJECT_SRC}/ld128")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
target_include_directories("${PROJECT_NAME}" PUBLIC ${OPENLIBM_INCLUDE_DIRS})
|
||||
|
||||
file(GLOB PUBLIC_HEADERS "*.h" "include/*.h" "${OPENLIBM_ARCH_FOLDER}/*.h" "src/*.h")
|
||||
set_target_properties("${PROJECT_NAME}" PROPERTIES PUBLIC_HEADER "${PUBLIC_HEADERS}")
|
||||
install (TARGETS "${PROJECT_NAME}")
|
||||
|
||||
# Can't use configure_file because openlibm.pc.in uses $var instead of CMake configure @var's
|
||||
# Would rather string replace variables here instead of editing .pc.in, because editing .pc.in
|
||||
# might build break autotools build.
|
||||
file(READ "${PROJECT_SRC}/openlibm.pc.in" PC_FILE)
|
||||
string(REPLACE "\${version}" ${CMAKE_PROJECT_VERSION} PC_FILE ${PC_FILE})
|
||||
string(PREPEND PC_FILE "prefix=${CMAKE_INSTALL_PREFIX}
|
||||
includedir=\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}
|
||||
libdir=\${prefix}/${CMAKE_INSTALL_LIBDIR}\n
|
||||
")
|
||||
file(WRITE "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.pc" ${PC_FILE})
|
||||
install(FILES "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.pc"
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
|
115
src/openlibm/LICENSE.md
Normal file
115
src/openlibm/LICENSE.md
Normal file
|
@ -0,0 +1,115 @@
|
|||
## OpenLibm
|
||||
|
||||
OpenLibm contains code that is covered by various licenses.
|
||||
|
||||
The OpenLibm code derives from the FreeBSD msun and OpenBSD libm
|
||||
implementations, which in turn derives from FDLIBM 5.3. As a result, it
|
||||
has a number of fixes and updates that have accumulated over the years
|
||||
in msun, and also optimized assembly versions of many functions. These
|
||||
improvements are provided under the BSD and ISC licenses. The msun
|
||||
library also includes work placed under the public domain, which is
|
||||
noted in the individual files. Further work on making a standalone
|
||||
OpenLibm library from msun, as part of the Julia project is covered
|
||||
under the MIT license. The test files, test-double.c and test-float.c
|
||||
are under the LGPL.
|
||||
|
||||
## Parts copyrighted by the Julia project (MIT License)
|
||||
|
||||
> Copyright (c) 2011-14 The Julia Project.
|
||||
> https://github.com/JuliaMath/openlibm/graphs/contributors
|
||||
>
|
||||
> 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.
|
||||
|
||||
## Parts copyrighted by Stephen L. Moshier (ISC License)
|
||||
|
||||
> Copyright (c) 2008 Stephen L. Moshier <steve@moshier.net>
|
||||
>
|
||||
> Permission to use, copy, modify, and distribute this software for any
|
||||
> purpose with or without fee is hereby granted, provided that the above
|
||||
> copyright notice and this permission notice appear in all copies.
|
||||
>
|
||||
> THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
> WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
> MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
> ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
> WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
> ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
> OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
## FREEBSD MSUN (FreeBSD/2-clause BSD/Simplified BSD License)
|
||||
|
||||
> Copyright 1992-2011 The FreeBSD Project. All rights reserved.
|
||||
>
|
||||
> Redistribution and use in source and binary forms, with or without
|
||||
> modification, are permitted provided that the following conditions are
|
||||
> met:
|
||||
>
|
||||
> 1. Redistributions of source code must retain the above copyright
|
||||
> notice, this list of conditions and the following disclaimer.
|
||||
>
|
||||
> 2. Redistributions in binary form must reproduce the above copyright
|
||||
> notice, 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 FREEBSD PROJECT ``AS IS'' AND ANY
|
||||
> EXPRESS OR 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 FREEBSD PROJECT OR
|
||||
> CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
> EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
> PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
> PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
> LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
> NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
> SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
>
|
||||
> The views and conclusions contained in the software and documentation
|
||||
> are those of the authors and should not be interpreted as representing
|
||||
> official policies, either expressed or implied, of the FreeBSD
|
||||
> Project.
|
||||
|
||||
## FDLIBM
|
||||
|
||||
> Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
>
|
||||
> Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||
> Permission to use, copy, modify, and distribute this
|
||||
> software is freely granted, provided that this notice
|
||||
> is preserved.
|
||||
|
||||
## Tests
|
||||
|
||||
> Copyright (C) 1997, 1999 Free Software Foundation, Inc.
|
||||
> This file is part of the GNU C Library.
|
||||
> Contributed by Andreas Jaeger <aj@suse.de>, 1997.
|
||||
>
|
||||
> The GNU C 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.
|
||||
>
|
||||
> The GNU C 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 the GNU C Library; if not, write to the Free
|
||||
> Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
> 02111-1307 USA.
|
197
src/openlibm/Make.inc
Normal file
197
src/openlibm/Make.inc
Normal file
|
@ -0,0 +1,197 @@
|
|||
# -*- mode: makefile-gmake -*-
|
||||
# vi:ft=make
|
||||
|
||||
# Default build rule for any Makefile in this project: all
|
||||
default: all
|
||||
|
||||
OS := $(shell uname)
|
||||
# Do not forget to bump SOMINOR when changing VERSION,
|
||||
# and SOMAJOR when breaking ABI in a backward-incompatible way
|
||||
VERSION = 0.8.0
|
||||
SOMAJOR = 4
|
||||
SOMINOR = 0
|
||||
DESTDIR =
|
||||
prefix ?= /usr/local
|
||||
bindir ?= $(prefix)/bin
|
||||
libdir ?= $(prefix)/lib
|
||||
includedir ?= $(prefix)/include
|
||||
|
||||
ifeq ($(OS), FreeBSD)
|
||||
pkgconfigdir ?= $(prefix)/libdata/pkgconfig
|
||||
else
|
||||
pkgconfigdir ?= $(libdir)/pkgconfig
|
||||
endif
|
||||
|
||||
# Build with Code Coverage
|
||||
# Only test with Ubuntu + gcc + lcov, may not work for other platform.
|
||||
# deps: https://github.com/linux-test-project/lcov
|
||||
# You don't need to set this flag manually, `make coverage` will do it for you.
|
||||
# Just Run: make clean && make coverage -j
|
||||
CODE_COVERAGE ?= 0
|
||||
|
||||
ifneq (,$(findstring $(OS),Darwin FreeBSD OpenBSD))
|
||||
USEGCC ?= 0
|
||||
USECLANG ?= 1
|
||||
endif
|
||||
|
||||
ifneq (,$(findstring CLANG,$(MSYSTEM)))
|
||||
# In MSYS2
|
||||
USEGCC = 0
|
||||
USECLANG = 1
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),wasm32)
|
||||
USECLANG = 1
|
||||
USEGCC = 0
|
||||
TOOLPREFIX = llvm-
|
||||
endif
|
||||
|
||||
USEGCC ?= 1
|
||||
USECLANG ?= 0
|
||||
TOOLPREFIX ?=
|
||||
|
||||
AR := $(TOOLPREFIX)ar
|
||||
|
||||
ifeq ($(USECLANG),1)
|
||||
USEGCC = 0
|
||||
CC = clang
|
||||
CFLAGS_add += -fno-builtin -fno-strict-aliasing
|
||||
endif
|
||||
|
||||
ifeq ($(USEGCC),1)
|
||||
CC := $(TOOLPREFIX)gcc
|
||||
CFLAGS_add += -fno-gnu89-inline -fno-builtin
|
||||
endif
|
||||
|
||||
ARCH ?= $(shell $(CC) -dumpmachine | sed "s/\([^-]*\).*$$/\1/")
|
||||
|
||||
ifeq ($(ARCH),mingw32)
|
||||
$(error "the mingw32 compiler you are using fails the openblas testsuite. please see the Julia README.windows.md document for a replacement")
|
||||
endif
|
||||
|
||||
# OS-specific stuff
|
||||
ifeq ($(ARCH),arm64)
|
||||
override ARCH := aarch64
|
||||
endif
|
||||
ifeq ($(findstring arm,$(ARCH)),arm)
|
||||
override ARCH := arm
|
||||
MARCH ?= armv7-a+fp
|
||||
CFLAGS_add += -mhard-float
|
||||
endif
|
||||
ifeq ($(findstring powerpc,$(ARCH)),powerpc)
|
||||
override ARCH := powerpc
|
||||
endif
|
||||
ifeq ($(findstring ppc,$(ARCH)),ppc)
|
||||
override ARCH := powerpc
|
||||
endif
|
||||
ifeq ($(findstring s390,$(ARCH)),s390)
|
||||
override ARCH := s390
|
||||
endif
|
||||
ifneq ($(filter $(ARCH),i386 i486 i586 i686 i387 i487 i587 i687),)
|
||||
override ARCH := i387
|
||||
MARCH ?= i686
|
||||
endif
|
||||
ifeq ($(ARCH),x86_64)
|
||||
override ARCH := amd64
|
||||
endif
|
||||
ifeq ($(findstring mips,$(ARCH)),mips)
|
||||
override ARCH := mips
|
||||
endif
|
||||
ifeq ($(findstring riscv64,$(ARCH)),riscv64)
|
||||
override ARCH := riscv64
|
||||
endif
|
||||
ifeq ($(findstring loongarch64,$(ARCH)),loongarch64)
|
||||
override ARCH := loongarch64
|
||||
endif
|
||||
|
||||
# If CFLAGS does not contain a -O optimization flag, default to -O3
|
||||
ifeq ($(findstring -O,$(CFLAGS)),)
|
||||
CFLAGS_add += -O3
|
||||
endif
|
||||
|
||||
ifneq (,$(findstring MINGW,$(OS)))
|
||||
override OS=WINNT
|
||||
endif
|
||||
|
||||
#keep these if statements separate
|
||||
ifeq ($(OS), WINNT)
|
||||
SHLIB_EXT = dll
|
||||
SONAME_FLAG =
|
||||
shlibdir = $(bindir)
|
||||
else
|
||||
ifeq ($(OS), Darwin)
|
||||
SHLIB_EXT = dylib
|
||||
SONAME_FLAG = -install_name
|
||||
else
|
||||
SHLIB_EXT = so
|
||||
SONAME_FLAG = -soname
|
||||
endif
|
||||
CFLAGS_add += -fPIC
|
||||
shlibdir = $(libdir)
|
||||
endif
|
||||
|
||||
# Add `-march` to our CFLAGS if it's defined
|
||||
ifneq ($(MARCH),)
|
||||
CFLAGS_arch += -march=$(MARCH)
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),i387)
|
||||
CFLAGS_arch += -m32
|
||||
SFLAGS_arch += -m32
|
||||
LDFLAGS_arch += -m32
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),amd64)
|
||||
CFLAGS_arch += -m64
|
||||
SFLAGS_arch += -m64
|
||||
LDFLAGS_arch += -m64
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),wasm32)
|
||||
CFLAGS_arch += -ffreestanding -nostdlib -nostdinc --target=wasm32-unknown-unknown
|
||||
endif
|
||||
|
||||
# Add our "arch"-related FLAGS in. We separate arch-related flags out so that
|
||||
# we can conveniently get at them for targets that don't want the rest of
|
||||
# *FLAGS_add, such as the testing Makefile targets
|
||||
CFLAGS_add += $(CFLAGS_arch)
|
||||
SFLAGS_add += $(SFLAGS_arch)
|
||||
LDFLAGS_add += $(LDFLAGS_arch)
|
||||
|
||||
CFLAGS_add += -std=c99 -Wall -I$(OPENLIBM_HOME) -I$(OPENLIBM_HOME)/include -I$(OPENLIBM_HOME)/$(ARCH) -I$(OPENLIBM_HOME)/src -DASSEMBLER -D__BSD_VISIBLE -Wno-implicit-function-declaration
|
||||
ifneq ($(filter $(ARCH),i387 amd64 powerpc),)
|
||||
CFLAGS_add += -I$(OPENLIBM_HOME)/ld80
|
||||
else
|
||||
ifneq ($(filter $(ARCH),aarch64 riscv64),)
|
||||
CFLAGS_add += -I$(OPENLIBM_HOME)/ld128
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(filter $(ARCH),i387 amd64),)
|
||||
# Determines whether `long double` is the same as `double` on this arch.
|
||||
# linux x86_64, for instance, `long double` is 80 bits wide, whereas on macOS aarch64,
|
||||
# `long double` is the same as `double`.
|
||||
LONG_DOUBLE_NOT_DOUBLE := 1
|
||||
else ifeq ($(ARCH), aarch64)
|
||||
ifeq ($(filter $(OS),Darwin WINNT),)
|
||||
LONG_DOUBLE_NOT_DOUBLE := 1
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(CODE_COVERAGE),1)
|
||||
CFLAGS_add += -g -O0 --coverage
|
||||
LDFLAGS_add += --coverage
|
||||
endif # CODE_COVERAGE==1
|
||||
|
||||
|
||||
%.c.o: %.c
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_add) -c $< -o $@
|
||||
|
||||
%.S.o: %.S
|
||||
$(CC) $(CPPFLAGS) $(SFLAGS) $(SFLAGS_add) $(filter -m% -B% -I% -D%,$(CFLAGS_add)) -c $< -o $@
|
||||
|
||||
|
||||
# Makefile debugging trick:
|
||||
# call print-VARIABLE to see the runtime value of any variable
|
||||
print-%:
|
||||
@echo '$*=$($*)'
|
140
src/openlibm/Makefile
Normal file
140
src/openlibm/Makefile
Normal file
|
@ -0,0 +1,140 @@
|
|||
OPENLIBM_HOME=$(abspath .)
|
||||
include ./Make.inc
|
||||
|
||||
SUBDIRS = src $(ARCH) bsdsrc
|
||||
ifeq ($(LONG_DOUBLE_NOT_DOUBLE),1)
|
||||
# Add ld80 directory on x86 and x64
|
||||
ifneq ($(filter $(ARCH),i387 amd64),)
|
||||
SUBDIRS += ld80
|
||||
else
|
||||
ifneq ($(filter $(ARCH),aarch64),)
|
||||
SUBDIRS += ld128
|
||||
else
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
define INC_template
|
||||
TEST=test
|
||||
override CUR_SRCS = $(1)_SRCS
|
||||
include $(1)/Make.files
|
||||
SRCS += $$(addprefix $(1)/,$$($(1)_SRCS))
|
||||
endef
|
||||
|
||||
DIR=test
|
||||
|
||||
$(foreach dir,$(SUBDIRS),$(eval $(call INC_template,$(dir))))
|
||||
|
||||
DUPLICATE_NAMES = $(filter $(patsubst %.S,%,$($(ARCH)_SRCS)),$(patsubst %.c,%,$(src_SRCS)))
|
||||
DUPLICATE_SRCS = $(addsuffix .c,$(DUPLICATE_NAMES))
|
||||
|
||||
OBJS = $(patsubst %.f,%.f.o,\
|
||||
$(patsubst %.S,%.S.o,\
|
||||
$(patsubst %.c,%.c.o,$(filter-out $(addprefix src/,$(DUPLICATE_SRCS)),$(SRCS)))))
|
||||
|
||||
# If we're on windows, don't do versioned shared libraries. Also, generate an import library
|
||||
# for the DLL. If we're on OSX, put the version number before the .dylib. Otherwise,
|
||||
# put it after.
|
||||
ifeq ($(OS), WINNT)
|
||||
OLM_MAJOR_MINOR_SHLIB_EXT := $(SHLIB_EXT)
|
||||
LDFLAGS_add += -Wl,--out-implib,libopenlibm.$(OLM_MAJOR_MINOR_SHLIB_EXT).a
|
||||
else
|
||||
ifeq ($(OS), Darwin)
|
||||
OLM_MAJOR_MINOR_SHLIB_EXT := $(SOMAJOR).$(SOMINOR).$(SHLIB_EXT)
|
||||
OLM_MAJOR_SHLIB_EXT := $(SOMAJOR).$(SHLIB_EXT)
|
||||
else
|
||||
OLM_MAJOR_MINOR_SHLIB_EXT := $(SHLIB_EXT).$(SOMAJOR).$(SOMINOR)
|
||||
OLM_MAJOR_SHLIB_EXT := $(SHLIB_EXT).$(SOMAJOR)
|
||||
endif
|
||||
LDFLAGS_add += -Wl,$(SONAME_FLAG),libopenlibm.$(OLM_MAJOR_SHLIB_EXT)
|
||||
endif
|
||||
|
||||
.PHONY: all check test clean distclean \
|
||||
install install-static install-shared install-pkgconfig install-headers
|
||||
|
||||
|
||||
OLM_LIBS := libopenlibm.a
|
||||
ifneq ($(ARCH), wasm32)
|
||||
OLM_LIBS += libopenlibm.$(OLM_MAJOR_MINOR_SHLIB_EXT)
|
||||
endif
|
||||
|
||||
all : $(OLM_LIBS)
|
||||
|
||||
check test: test/test-double test/test-float
|
||||
test/test-double
|
||||
test/test-float
|
||||
|
||||
libopenlibm.a: $(OBJS)
|
||||
$(AR) -rcs libopenlibm.a $(OBJS)
|
||||
|
||||
libopenlibm.$(OLM_MAJOR_MINOR_SHLIB_EXT): $(OBJS)
|
||||
$(CC) -shared $(OBJS) $(LDFLAGS) $(LDFLAGS_add) -o $@
|
||||
ifneq ($(OS),WINNT)
|
||||
ln -sf $@ libopenlibm.$(OLM_MAJOR_SHLIB_EXT)
|
||||
ln -sf $@ libopenlibm.$(SHLIB_EXT)
|
||||
endif
|
||||
|
||||
test/test-double: libopenlibm.$(OLM_MAJOR_MINOR_SHLIB_EXT)
|
||||
$(MAKE) -C test test-double
|
||||
|
||||
test/test-float: libopenlibm.$(OLM_MAJOR_MINOR_SHLIB_EXT)
|
||||
$(MAKE) -C test test-float
|
||||
|
||||
COVERAGE_DIR:=cov-html
|
||||
COVERAGE_FILE:=$(COVERAGE_DIR)/libopenlibm.info
|
||||
# Gen cov report with: make clean && make coverage -j
|
||||
coverage: clean-coverage
|
||||
$(MAKE) test CODE_COVERAGE=1
|
||||
$(MAKE) gen-cov-report
|
||||
|
||||
gen-cov-report:
|
||||
-mkdir $(COVERAGE_DIR)
|
||||
lcov -d amd64 -d bsdsrc -d ld80 -d src \
|
||||
--rc lcov_branch_coverage=1 --capture --output-file $(COVERAGE_FILE)
|
||||
genhtml --legend --branch-coverage \
|
||||
--title "Openlibm commit `git rev-parse HEAD`" \
|
||||
--output-directory $(COVERAGE_DIR)/ \
|
||||
$(COVERAGE_FILE)
|
||||
|
||||
# Zero coverage statistics and Delete report
|
||||
clean-coverage:
|
||||
-lcov -d amd64 -d bsdsrc -d ld80 -d src --zerocounters
|
||||
rm -f ./*/*.gcda
|
||||
rm -rf $(COVERAGE_DIR)/
|
||||
|
||||
clean: clean-coverage
|
||||
rm -f aarch64/*.o amd64/*.o arm/*.o bsdsrc/*.o i387/*.o loongarch64/*.o ld80/*.o ld128/*.o src/*.o powerpc/*.o mips/*.o s390/*.o riscv64/*.o
|
||||
rm -f libopenlibm.a libopenlibm.*$(SHLIB_EXT)*
|
||||
rm -f ./*/*.gcno
|
||||
$(MAKE) -C test clean
|
||||
|
||||
openlibm.pc: openlibm.pc.in Make.inc Makefile
|
||||
echo "version=${VERSION}" > openlibm.pc
|
||||
echo "libdir=$(DESTDIR)$(libdir)" >> openlibm.pc
|
||||
echo "includedir=$(DESTDIR)$(includedir)/openlibm" >> openlibm.pc
|
||||
cat openlibm.pc.in >> openlibm.pc
|
||||
|
||||
install-static: libopenlibm.a
|
||||
mkdir -p $(DESTDIR)$(libdir)
|
||||
cp -RpP -f libopenlibm.a $(DESTDIR)$(libdir)/
|
||||
|
||||
install-shared: libopenlibm.$(OLM_MAJOR_MINOR_SHLIB_EXT)
|
||||
mkdir -p $(DESTDIR)$(shlibdir)
|
||||
ifeq ($(OS), WINNT)
|
||||
mkdir -p $(DESTDIR)$(libdir)
|
||||
cp -RpP -f libopenlibm.*$(SHLIB_EXT) $(DESTDIR)$(shlibdir)/
|
||||
cp -RpP -f libopenlibm.*$(SHLIB_EXT).a $(DESTDIR)$(libdir)/
|
||||
else
|
||||
cp -RpP -f libopenlibm.*$(SHLIB_EXT)* $(DESTDIR)$(shlibdir)/
|
||||
endif
|
||||
|
||||
install-pkgconfig: openlibm.pc
|
||||
mkdir -p $(DESTDIR)$(pkgconfigdir)
|
||||
cp -RpP -f openlibm.pc $(DESTDIR)$(pkgconfigdir)/
|
||||
|
||||
install-headers:
|
||||
mkdir -p $(DESTDIR)$(includedir)/openlibm
|
||||
cp -RpP -f include/*.h $(DESTDIR)$(includedir)/openlibm
|
||||
cp -RpP -f src/*.h $(DESTDIR)$(includedir)/openlibm
|
||||
|
||||
install: install-static install-shared install-pkgconfig install-headers
|
71
src/openlibm/README.md
Normal file
71
src/openlibm/README.md
Normal file
|
@ -0,0 +1,71 @@
|
|||
# OpenLibm
|
||||
|
||||
[](https://codecov.io/gh/JuliaMath/openlibm)
|
||||
|
||||
[OpenLibm](https://openlibm.org/) is an effort to have a high quality, portable, standalone
|
||||
C mathematical library ([`libm`](http://en.wikipedia.org/wiki/libm)).
|
||||
It can be used standalone in applications and programming language
|
||||
implementations.
|
||||
|
||||
The project was born out of a need to have a good `libm` for the
|
||||
[Julia programming language](http://www.julialang.org) that worked
|
||||
consistently across compilers and operating systems, and in 32-bit and
|
||||
64-bit environments.
|
||||
|
||||
## Platform support
|
||||
|
||||
OpenLibm builds on Linux, macOS, Windows, FreeBSD, OpenBSD, NetBSD, and
|
||||
DragonFly BSD. It builds with both GCC and clang. Although largely
|
||||
tested and widely used on the x86 and x86-64 architectures, OpenLibm
|
||||
also supports arm, aarch64, ppc64le, mips, wasm32, riscv, s390(x) and
|
||||
loongarch64.
|
||||
|
||||
## Build instructions
|
||||
|
||||
### GNU Make
|
||||
|
||||
1. Use GNU Make to build OpenLibm. This is `make` on most systems, but `gmake` on BSDs.
|
||||
2. Use `make USEGCC=1` to build with GCC. This is the default on
|
||||
Linux and Windows.
|
||||
3. Use `make USECLANG=1` to build with clang. This is the default on OS X, FreeBSD,
|
||||
and OpenBSD.
|
||||
4. Use `make ARCH=wasm32` to build the wasm32 library with clang.
|
||||
5. Architectures are auto-detected. Use `make ARCH=i386` to force a
|
||||
build for i386. Other supported architectures are i486, i586, and
|
||||
i686. GCC 4.8 is the minimum requirement for correct codegen on
|
||||
older 32-bit architectures.
|
||||
|
||||
|
||||
**Cross Build**
|
||||
Take `riscv64` as example:
|
||||
1. install `qemu-riscv64-static`, `gcc-riscv64-linux-gnu`
|
||||
2. Cross build:
|
||||
```sh
|
||||
ARCH=riscv64
|
||||
TRIPLE=$ARCH-linux-gnu
|
||||
make ARCH=$ARCH TOOLPREFIX=$TRIPLE- -j
|
||||
make -C test ARCH=$ARCH TOOLPREFIX=$TRIPLE- -j
|
||||
```
|
||||
|
||||
3. Run test with qemu:
|
||||
```sh
|
||||
qemu-$ARCH-static -L . -L /usr/$TRIPLE/ test/test-float
|
||||
qemu-$ARCH-static -L . -L /usr/$TRIPLE/ test/test-double
|
||||
```
|
||||
|
||||
|
||||
### CMake
|
||||
|
||||
1. Create build directory with `mkdir build` and navigate into it with `cd build`.
|
||||
2. Run CMake to configure project and generate native build system with `cmake /path/to/openlibm/`
|
||||
or generate project with build system of choice e.g. `cmake /path/to/openlib/ -G "MinGW Makefiles"`.
|
||||
3. Build with the build system with `cmake --build .`.
|
||||
|
||||
Default CMake configuration builds a shared library, this can easily be configured using
|
||||
[BUILD_SHARED_LIBS](https://cmake.org/cmake/help/latest/variable/BUILD_SHARED_LIBS.html)
|
||||
configuration option.
|
||||
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
PowerPC support for openlibm was graciously sponsored by IBM.
|
1
src/openlibm/aarch64/Make.files
Normal file
1
src/openlibm/aarch64/Make.files
Normal file
|
@ -0,0 +1 @@
|
|||
$(CUR_SRCS) = fenv.c
|
51
src/openlibm/aarch64/fenv.c
Normal file
51
src/openlibm/aarch64/fenv.c
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*-
|
||||
* Copyright (c) 2004 David Schultz <das@FreeBSD.ORG>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, 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 AUTHOR 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 AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD: src/lib/msun/arm/fenv.c,v 1.3 2011/10/16 05:37:56 das Exp $
|
||||
*/
|
||||
|
||||
#include <openlibm_fenv.h>
|
||||
|
||||
#ifdef __GNUC_GNU_INLINE__
|
||||
#error "This file must be compiled with C99 'inline' semantics"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Hopefully the system ID byte is immutable, so it's valid to use
|
||||
* this as a default environment.
|
||||
*/
|
||||
const fenv_t __fe_dfl_env = {0};
|
||||
|
||||
extern inline int feclearexcept(int __excepts);
|
||||
extern inline int fegetexceptflag(fexcept_t *__flagp, int __excepts);
|
||||
extern inline int fesetexceptflag(const fexcept_t *__flagp, int __excepts);
|
||||
extern inline int feraiseexcept(int __excepts);
|
||||
extern inline int fetestexcept(int __excepts);
|
||||
extern inline int fegetround(void);
|
||||
extern inline int fesetround(int __round);
|
||||
extern inline int fegetenv(fenv_t *__envp);
|
||||
extern inline int feholdexcept(fenv_t *__envp);
|
||||
extern inline int fesetenv(const fenv_t *__envp);
|
||||
extern inline int feupdateenv(const fenv_t *__envp);
|
7
src/openlibm/amd64/Make.files
Normal file
7
src/openlibm/amd64/Make.files
Normal file
|
@ -0,0 +1,7 @@
|
|||
$(CUR_SRCS) = fenv.c e_remainder.S e_remainderf.S e_remainderl.S \
|
||||
e_sqrt.S e_sqrtf.S e_sqrtl.S \
|
||||
s_llrint.S s_llrintf.S s_llrintl.S \
|
||||
s_logbl.S s_lrint.S s_lrintf.S s_lrintl.S \
|
||||
s_remquo.S s_remquof.S s_remquol.S \
|
||||
s_rintl.S s_scalbn.S s_scalbnf.S s_scalbnl.S \
|
||||
e_fmod.S e_fmodf.S e_fmodl.S
|
110
src/openlibm/amd64/bsd_asm.h
Normal file
110
src/openlibm/amd64/bsd_asm.h
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*-
|
||||
* Copyright (c) 1990 The Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* William Jolitz.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 4. Neither the name of the University 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 REGENTS 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 REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (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: @(#)DEFS.h 5.1 (Berkeley) 4/23/90
|
||||
* $FreeBSD: src/sys/amd64/include/asm.h,v 1.18 2007/08/22 04:26:07 jkoshy Exp $
|
||||
*/
|
||||
|
||||
#ifndef _BSD_ASM_H_
|
||||
#define _BSD_ASM_H_
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "../i387/osx_asm.h"
|
||||
#define CNAME(x) EXT(x)
|
||||
#else
|
||||
#include "bsd_cdefs.h"
|
||||
|
||||
#ifdef PIC
|
||||
#define PIC_PLT(x) x@PLT
|
||||
#define PIC_GOT(x) x@GOTPCREL(%rip)
|
||||
#else
|
||||
#define PIC_PLT(x) x
|
||||
#define PIC_GOT(x) x
|
||||
#endif
|
||||
|
||||
/*
|
||||
* CNAME and HIDENAME manage the relationship between symbol names in C
|
||||
* and the equivalent assembly language names. CNAME is given a name as
|
||||
* it would be used in a C program. It expands to the equivalent assembly
|
||||
* language name. HIDENAME is given an assembly-language name, and expands
|
||||
* to a possibly-modified form that will be invisible to C programs.
|
||||
*/
|
||||
#define CNAME(csym) csym
|
||||
#define HIDENAME(asmsym) .asmsym
|
||||
|
||||
#define _START_ENTRY .p2align 4,0x90
|
||||
|
||||
#if defined(__ELF__)
|
||||
#define _ENTRY(x) .text; _START_ENTRY; \
|
||||
.globl CNAME(x); .type CNAME(x),@function; CNAME(x):
|
||||
#define END(x) .size x, . - x
|
||||
|
||||
#elif defined(_WIN32)
|
||||
#ifndef _MSC_VER
|
||||
#define END(x) .end
|
||||
#define _START_ENTRY_WIN .text; _START_ENTRY
|
||||
#else
|
||||
#define END(x) end
|
||||
#define _START_ENTRY_WIN .code; _START_ENTRY
|
||||
#endif
|
||||
#define _ENTRY(x) _START_ENTRY_WIN; \
|
||||
.globl CNAME(x); .section .drectve; .ascii " -export:", #x; \
|
||||
.section .text; .def CNAME(x); .scl 2; .type 32; .endef; CNAME(x):
|
||||
#endif
|
||||
|
||||
#ifdef PROF
|
||||
#define ALTENTRY(x) _ENTRY(x); \
|
||||
pushq %rbp; movq %rsp,%rbp; \
|
||||
call PIC_PLT(HIDENAME(mcount)); \
|
||||
popq %rbp; \
|
||||
jmp 9f
|
||||
#define ENTRY(x) _ENTRY(x); \
|
||||
pushq %rbp; movq %rsp,%rbp; \
|
||||
call PIC_PLT(HIDENAME(mcount)); \
|
||||
popq %rbp; \
|
||||
9:
|
||||
#else
|
||||
#define ALTENTRY(x) _ENTRY(x)
|
||||
#define ENTRY(x) _ENTRY(x)
|
||||
#endif
|
||||
|
||||
|
||||
#define RCSID(x) .text; .asciz x
|
||||
|
||||
#undef __FBSDID
|
||||
#if !defined(lint) && !defined(STRIP_FBSDID)
|
||||
#define __FBSDID(s) .ident s
|
||||
#else
|
||||
#define __FBSDID(s) /* nothing */
|
||||
#endif /* not lint and not STRIP_FBSDID */
|
||||
|
||||
#endif
|
||||
#endif /* !_BSD_ASM_H_ */
|
218
src/openlibm/amd64/bsd_fpu.h
Normal file
218
src/openlibm/amd64/bsd_fpu.h
Normal file
|
@ -0,0 +1,218 @@
|
|||
/*-
|
||||
* Copyright (c) 1990 The Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* William Jolitz.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 4. Neither the name of the University 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 REGENTS 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 REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (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: @(#)npx.h 5.3 (Berkeley) 1/18/91
|
||||
* $FreeBSD: src/sys/x86/include/fpu.h,v 1.1 2012/03/16 20:24:30 tijl Exp $
|
||||
*/
|
||||
|
||||
/*
|
||||
* Floating Point Data Structures and Constants
|
||||
* W. Jolitz 1/90
|
||||
*/
|
||||
|
||||
#ifndef _BSD_FPU_H_
|
||||
#define _BSD_FPU_H_
|
||||
|
||||
#include "types-compat.h"
|
||||
|
||||
/* Environment information of floating point unit. */
|
||||
struct env87 {
|
||||
int32_t en_cw; /* control word (16bits) */
|
||||
int32_t en_sw; /* status word (16bits) */
|
||||
int32_t en_tw; /* tag word (16bits) */
|
||||
int32_t en_fip; /* fp instruction pointer */
|
||||
uint16_t en_fcs; /* fp code segment selector */
|
||||
uint16_t en_opcode; /* opcode last executed (11 bits) */
|
||||
int32_t en_foo; /* fp operand offset */
|
||||
int32_t en_fos; /* fp operand segment selector */
|
||||
};
|
||||
|
||||
/* Contents of each x87 floating point accumulator. */
|
||||
struct fpacc87 {
|
||||
uint8_t fp_bytes[10];
|
||||
};
|
||||
|
||||
/* Floating point context. (i386 fnsave/frstor) */
|
||||
struct save87 {
|
||||
struct env87 sv_env; /* floating point control/status */
|
||||
struct fpacc87 sv_ac[8]; /* accumulator contents, 0-7 */
|
||||
uint8_t sv_pad0[4]; /* saved status word (now unused) */
|
||||
/*
|
||||
* Bogus padding for emulators. Emulators should use their own
|
||||
* struct and arrange to store into this struct (ending here)
|
||||
* before it is inspected for ptracing or for core dumps. Some
|
||||
* emulators overwrite the whole struct. We have no good way of
|
||||
* knowing how much padding to leave. Leave just enough for the
|
||||
* GPL emulator's i387_union (176 bytes total).
|
||||
*/
|
||||
uint8_t sv_pad[64]; /* padding; used by emulators */
|
||||
};
|
||||
|
||||
/* Contents of each SSE extended accumulator. */
|
||||
struct xmmacc {
|
||||
uint8_t xmm_bytes[16];
|
||||
};
|
||||
|
||||
/* Contents of the upper 16 bytes of each AVX extended accumulator. */
|
||||
struct ymmacc {
|
||||
uint8_t ymm_bytes[16];
|
||||
};
|
||||
|
||||
/* Rename structs below depending on machine architecture. */
|
||||
#ifdef __i386__
|
||||
#define __envxmm32 envxmm
|
||||
#else
|
||||
#define __envxmm32 envxmm32
|
||||
#define __envxmm64 envxmm
|
||||
#endif
|
||||
|
||||
struct __envxmm32 {
|
||||
uint16_t en_cw; /* control word (16bits) */
|
||||
uint16_t en_sw; /* status word (16bits) */
|
||||
uint16_t en_tw; /* tag word (16bits) */
|
||||
uint16_t en_opcode; /* opcode last executed (11 bits) */
|
||||
uint32_t en_fip; /* fp instruction pointer */
|
||||
uint16_t en_fcs; /* fp code segment selector */
|
||||
uint16_t en_pad0; /* padding */
|
||||
uint32_t en_foo; /* fp operand offset */
|
||||
uint16_t en_fos; /* fp operand segment selector */
|
||||
uint16_t en_pad1; /* padding */
|
||||
uint32_t en_mxcsr; /* SSE control/status register */
|
||||
uint32_t en_mxcsr_mask; /* valid bits in mxcsr */
|
||||
};
|
||||
|
||||
struct __envxmm64 {
|
||||
uint16_t en_cw; /* control word (16bits) */
|
||||
uint16_t en_sw; /* status word (16bits) */
|
||||
uint8_t en_tw; /* tag word (8bits) */
|
||||
uint8_t en_zero;
|
||||
uint16_t en_opcode; /* opcode last executed (11 bits ) */
|
||||
uint64_t en_rip; /* fp instruction pointer */
|
||||
uint64_t en_rdp; /* fp operand pointer */
|
||||
uint32_t en_mxcsr; /* SSE control/status register */
|
||||
uint32_t en_mxcsr_mask; /* valid bits in mxcsr */
|
||||
};
|
||||
|
||||
/* Floating point context. (i386 fxsave/fxrstor) */
|
||||
struct savexmm {
|
||||
struct __envxmm32 sv_env;
|
||||
struct {
|
||||
struct fpacc87 fp_acc;
|
||||
uint8_t fp_pad[6]; /* padding */
|
||||
} sv_fp[8];
|
||||
struct xmmacc sv_xmm[8];
|
||||
uint8_t sv_pad[224];
|
||||
} __attribute__ ((aligned(16)));
|
||||
|
||||
#ifdef __i386__
|
||||
union savefpu {
|
||||
struct save87 sv_87;
|
||||
struct savexmm sv_xmm;
|
||||
};
|
||||
#else
|
||||
/* Floating point context. (amd64 fxsave/fxrstor) */
|
||||
struct savefpu {
|
||||
struct __envxmm64 sv_env;
|
||||
struct {
|
||||
struct fpacc87 fp_acc;
|
||||
uint8_t fp_pad[6]; /* padding */
|
||||
} sv_fp[8];
|
||||
struct xmmacc sv_xmm[16];
|
||||
uint8_t sv_pad[96];
|
||||
} __attribute__ ((aligned(16)));
|
||||
#endif
|
||||
|
||||
struct xstate_hdr {
|
||||
uint64_t xstate_bv;
|
||||
uint8_t xstate_rsrv0[16];
|
||||
uint8_t xstate_rsrv[40];
|
||||
};
|
||||
|
||||
struct savexmm_xstate {
|
||||
struct xstate_hdr sx_hd;
|
||||
struct ymmacc sx_ymm[16];
|
||||
};
|
||||
|
||||
struct savexmm_ymm {
|
||||
struct __envxmm32 sv_env;
|
||||
struct {
|
||||
struct fpacc87 fp_acc;
|
||||
int8_t fp_pad[6]; /* padding */
|
||||
} sv_fp[8];
|
||||
struct xmmacc sv_xmm[16];
|
||||
uint8_t sv_pad[96];
|
||||
struct savexmm_xstate sv_xstate;
|
||||
} __attribute__ ((aligned(16)));
|
||||
|
||||
struct savefpu_xstate {
|
||||
struct xstate_hdr sx_hd;
|
||||
struct ymmacc sx_ymm[16];
|
||||
};
|
||||
|
||||
struct savefpu_ymm {
|
||||
struct __envxmm64 sv_env;
|
||||
struct {
|
||||
struct fpacc87 fp_acc;
|
||||
int8_t fp_pad[6]; /* padding */
|
||||
} sv_fp[8];
|
||||
struct xmmacc sv_xmm[16];
|
||||
uint8_t sv_pad[96];
|
||||
struct savefpu_xstate sv_xstate;
|
||||
} __attribute__ ((aligned(64)));
|
||||
|
||||
#undef __envxmm32
|
||||
#undef __envxmm64
|
||||
|
||||
/*
|
||||
* The hardware default control word for i387's and later coprocessors is
|
||||
* 0x37F, giving:
|
||||
*
|
||||
* round to nearest
|
||||
* 64-bit precision
|
||||
* all exceptions masked.
|
||||
*
|
||||
* FreeBSD/i386 uses 53 bit precision for things like fadd/fsub/fsqrt etc
|
||||
* because of the difference between memory and fpu register stack arguments.
|
||||
* If its using an intermediate fpu register, it has 80/64 bits to work
|
||||
* with. If it uses memory, it has 64/53 bits to work with. However,
|
||||
* gcc is aware of this and goes to a fair bit of trouble to make the
|
||||
* best use of it.
|
||||
*
|
||||
* This is mostly academic for AMD64, because the ABI prefers the use
|
||||
* SSE2 based math. For FreeBSD/amd64, we go with the default settings.
|
||||
*/
|
||||
#define __INITIAL_FPUCW__ 0x037F
|
||||
#define __INITIAL_FPUCW_I386__ 0x127F
|
||||
#define __INITIAL_NPXCW__ __INITIAL_FPUCW_I386__
|
||||
#define __INITIAL_MXCSR__ 0x1F80
|
||||
#define __INITIAL_MXCSR_MASK__ 0xFFBF
|
||||
|
||||
#endif /* !_BSD_FPU_H_ */
|
272
src/openlibm/amd64/bsd_ieeefp.h
Normal file
272
src/openlibm/amd64/bsd_ieeefp.h
Normal file
|
@ -0,0 +1,272 @@
|
|||
/*-
|
||||
* Copyright (c) 2003 Peter Wemm.
|
||||
* Copyright (c) 1990 Andrew Moore, Talke Studio
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must 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 the University 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 REGENTS 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 REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (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: @(#) ieeefp.h 1.0 (Berkeley) 9/23/93
|
||||
* $FreeBSD: src/sys/amd64/include/ieeefp.h,v 1.14 2005/04/12 23:12:00 jhb Exp $
|
||||
*/
|
||||
|
||||
/*
|
||||
* IEEE floating point type and constant definitions.
|
||||
*/
|
||||
|
||||
#ifndef _BSD_IEEEFP_H_
|
||||
#define _BSD_IEEEFP_H_
|
||||
|
||||
/*
|
||||
* FP rounding modes
|
||||
*/
|
||||
typedef enum {
|
||||
FP_RN=0, /* round to nearest */
|
||||
FP_RM, /* round down to minus infinity */
|
||||
FP_RP, /* round up to plus infinity */
|
||||
FP_RZ /* truncate */
|
||||
} fp_rnd_t;
|
||||
|
||||
/*
|
||||
* FP precision modes
|
||||
*/
|
||||
typedef enum {
|
||||
FP_PS=0, /* 24 bit (single-precision) */
|
||||
FP_PRS, /* reserved */
|
||||
FP_PD, /* 53 bit (double-precision) */
|
||||
FP_PE /* 64 bit (extended-precision) */
|
||||
} fp_prec_t;
|
||||
|
||||
#define fp_except_t int
|
||||
|
||||
/*
|
||||
* FP exception masks
|
||||
*/
|
||||
#define FP_X_INV 0x01 /* invalid operation */
|
||||
#define FP_X_DNML 0x02 /* denormal */
|
||||
#define FP_X_DZ 0x04 /* zero divide */
|
||||
#define FP_X_OFL 0x08 /* overflow */
|
||||
#define FP_X_UFL 0x10 /* underflow */
|
||||
#define FP_X_IMP 0x20 /* (im)precision */
|
||||
#define FP_X_STK 0x40 /* stack fault */
|
||||
|
||||
/*
|
||||
* FP registers
|
||||
*/
|
||||
#define FP_MSKS_REG 0 /* exception masks */
|
||||
#define FP_PRC_REG 0 /* precision */
|
||||
#define FP_RND_REG 0 /* direction */
|
||||
#define FP_STKY_REG 1 /* sticky flags */
|
||||
|
||||
/*
|
||||
* FP register bit field masks
|
||||
*/
|
||||
#define FP_MSKS_FLD 0x3f /* exception masks field */
|
||||
#define FP_PRC_FLD 0x300 /* precision control field */
|
||||
#define FP_RND_FLD 0xc00 /* round control field */
|
||||
#define FP_STKY_FLD 0x3f /* sticky flags field */
|
||||
|
||||
/*
|
||||
* SSE mxcsr register bit field masks
|
||||
*/
|
||||
#define SSE_STKY_FLD 0x3f /* exception flags */
|
||||
#define SSE_DAZ_FLD 0x40 /* Denormals are zero */
|
||||
#define SSE_MSKS_FLD 0x1f80 /* exception masks field */
|
||||
#define SSE_RND_FLD 0x6000 /* rounding control */
|
||||
#define SSE_FZ_FLD 0x8000 /* flush to zero on underflow */
|
||||
|
||||
/*
|
||||
* FP register bit field offsets
|
||||
*/
|
||||
#define FP_MSKS_OFF 0 /* exception masks offset */
|
||||
#define FP_PRC_OFF 8 /* precision control offset */
|
||||
#define FP_RND_OFF 10 /* round control offset */
|
||||
#define FP_STKY_OFF 0 /* sticky flags offset */
|
||||
|
||||
/*
|
||||
* SSE mxcsr register bit field offsets
|
||||
*/
|
||||
#define SSE_STKY_OFF 0 /* exception flags offset */
|
||||
#define SSE_DAZ_OFF 6 /* DAZ exception mask offset */
|
||||
#define SSE_MSKS_OFF 7 /* other exception masks offset */
|
||||
#define SSE_RND_OFF 13 /* rounding control offset */
|
||||
#define SSE_FZ_OFF 15 /* flush to zero offset */
|
||||
|
||||
#if (defined(__GNUCLIKE_ASM) && defined(__CC_SUPPORTS___INLINE__)) || defined(_WIN32) \
|
||||
&& !defined(__cplusplus)
|
||||
|
||||
#define __fldenv(addr) __asm __volatile("fldenv %0" : : "m" (*(addr)))
|
||||
#define __fnstenv(addr) __asm __volatile("fnstenv %0" : "=m" (*(addr)))
|
||||
#define __fldcw(addr) __asm __volatile("fldcw %0" : : "m" (*(addr)))
|
||||
#define __fnstcw(addr) __asm __volatile("fnstcw %0" : "=m" (*(addr)))
|
||||
#define __fnstsw(addr) __asm __volatile("fnstsw %0" : "=m" (*(addr)))
|
||||
#define __ldmxcsr(addr) __asm __volatile("ldmxcsr %0" : : "m" (*(addr)))
|
||||
#define __stmxcsr(addr) __asm __volatile("stmxcsr %0" : "=m" (*(addr)))
|
||||
|
||||
/*
|
||||
* General notes about conflicting SSE vs FP status bits.
|
||||
* This code assumes that software will not fiddle with the control
|
||||
* bits of the SSE and x87 in such a way to get them out of sync and
|
||||
* still expect this to work. Break this at your peril.
|
||||
* Because I based this on the i386 port, the x87 state is used for
|
||||
* the fpget*() functions, and is shadowed into the SSE state for
|
||||
* the fpset*() functions. For dual source fpget*() functions, I
|
||||
* merge the two together. I think.
|
||||
*/
|
||||
|
||||
/* Set rounding control */
|
||||
static __inline__ fp_rnd_t
|
||||
__fpgetround(void)
|
||||
{
|
||||
unsigned short _cw;
|
||||
|
||||
__fnstcw(&_cw);
|
||||
return ((_cw & FP_RND_FLD) >> FP_RND_OFF);
|
||||
}
|
||||
|
||||
static __inline__ fp_rnd_t
|
||||
__fpsetround(fp_rnd_t _m)
|
||||
{
|
||||
unsigned short _cw;
|
||||
unsigned int _mxcsr;
|
||||
fp_rnd_t _p;
|
||||
|
||||
__fnstcw(&_cw);
|
||||
_p = (_cw & FP_RND_FLD) >> FP_RND_OFF;
|
||||
_cw &= ~FP_RND_FLD;
|
||||
_cw |= (_m << FP_RND_OFF) & FP_RND_FLD;
|
||||
__fldcw(&_cw);
|
||||
__stmxcsr(&_mxcsr);
|
||||
_mxcsr &= ~SSE_RND_FLD;
|
||||
_mxcsr |= (_m << SSE_RND_OFF) & SSE_RND_FLD;
|
||||
__ldmxcsr(&_mxcsr);
|
||||
return (_p);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set precision for fadd/fsub/fsqrt etc x87 instructions
|
||||
* There is no equivalent SSE mode or control.
|
||||
*/
|
||||
static __inline__ fp_prec_t
|
||||
__fpgetprec(void)
|
||||
{
|
||||
unsigned short _cw;
|
||||
|
||||
__fnstcw(&_cw);
|
||||
return ((_cw & FP_PRC_FLD) >> FP_PRC_OFF);
|
||||
}
|
||||
|
||||
static __inline__ fp_prec_t
|
||||
__fpsetprec(fp_rnd_t _m)
|
||||
{
|
||||
unsigned short _cw;
|
||||
fp_prec_t _p;
|
||||
|
||||
__fnstcw(&_cw);
|
||||
_p = (_cw & FP_PRC_FLD) >> FP_PRC_OFF;
|
||||
_cw &= ~FP_PRC_FLD;
|
||||
_cw |= (_m << FP_PRC_OFF) & FP_PRC_FLD;
|
||||
__fldcw(&_cw);
|
||||
return (_p);
|
||||
}
|
||||
|
||||
/*
|
||||
* Look at the exception masks
|
||||
* Note that x87 masks are inverse of the fp*() functions
|
||||
* API. ie: mask = 1 means disable for x87 and SSE, but
|
||||
* for the fp*() api, mask = 1 means enabled.
|
||||
*/
|
||||
static __inline__ fp_except_t
|
||||
__fpgetmask(void)
|
||||
{
|
||||
unsigned short _cw;
|
||||
|
||||
__fnstcw(&_cw);
|
||||
return ((~_cw) & FP_MSKS_FLD);
|
||||
}
|
||||
|
||||
static __inline__ fp_except_t
|
||||
__fpsetmask(fp_except_t _m)
|
||||
{
|
||||
unsigned short _cw;
|
||||
unsigned int _mxcsr;
|
||||
fp_except_t _p;
|
||||
|
||||
__fnstcw(&_cw);
|
||||
_p = (~_cw) & FP_MSKS_FLD;
|
||||
_cw &= ~FP_MSKS_FLD;
|
||||
_cw |= (~_m) & FP_MSKS_FLD;
|
||||
__fldcw(&_cw);
|
||||
__stmxcsr(&_mxcsr);
|
||||
/* XXX should we clear non-ieee SSE_DAZ_FLD and SSE_FZ_FLD ? */
|
||||
_mxcsr &= ~SSE_MSKS_FLD;
|
||||
_mxcsr |= ((~_m) << SSE_MSKS_OFF) & SSE_MSKS_FLD;
|
||||
__ldmxcsr(&_mxcsr);
|
||||
return (_p);
|
||||
}
|
||||
|
||||
/* See which sticky exceptions are pending, and reset them */
|
||||
static __inline__ fp_except_t
|
||||
__fpgetsticky(void)
|
||||
{
|
||||
unsigned short _sw;
|
||||
unsigned int _mxcsr;
|
||||
fp_except_t _ex;
|
||||
|
||||
__fnstsw(&_sw);
|
||||
_ex = _sw & FP_STKY_FLD;
|
||||
__stmxcsr(&_mxcsr);
|
||||
_ex |= _mxcsr & SSE_STKY_FLD;
|
||||
return (_ex);
|
||||
}
|
||||
|
||||
#endif /* __GNUCLIKE_ASM && __CC_SUPPORTS___INLINE__ && !__cplusplus */
|
||||
|
||||
#if !defined(__IEEEFP_NOINLINES__) && !defined(__cplusplus) \
|
||||
&& defined(__GNUCLIKE_ASM) && defined(__CC_SUPPORTS___INLINE__)
|
||||
|
||||
#define fpgetround() __fpgetround()
|
||||
#define fpsetround(_m) __fpsetround(_m)
|
||||
#define fpgetprec() __fpgetprec()
|
||||
#define fpsetprec(_m) __fpsetprec(_m)
|
||||
#define fpgetmask() __fpgetmask()
|
||||
#define fpsetmask(_m) __fpsetmask(_m)
|
||||
#define fpgetsticky() __fpgetsticky()
|
||||
|
||||
/* Suppress prototypes in the MI header. */
|
||||
#define _IEEEFP_INLINED_ 1
|
||||
|
||||
#else /* !__IEEEFP_NOINLINES__ && !__cplusplus && __GNUCLIKE_ASM
|
||||
&& __CC_SUPPORTS___INLINE__ */
|
||||
|
||||
/* Augment the userland declarations */
|
||||
__BEGIN_DECLS
|
||||
extern fp_prec_t fpgetprec(void);
|
||||
extern fp_prec_t fpsetprec(fp_prec_t);
|
||||
__END_DECLS
|
||||
|
||||
#endif /* !__IEEEFP_NOINLINES__ && !__cplusplus && __GNUCLIKE_ASM
|
||||
&& __CC_SUPPORTS___INLINE__ */
|
||||
|
||||
#endif /* !_BSD_IEEEFP_H_ */
|
56
src/openlibm/amd64/e_fmod.S
Normal file
56
src/openlibm/amd64/e_fmod.S
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (c) 1993,94 Winning Strategies, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must 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. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by Winning Strategies, Inc.
|
||||
* 4. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* 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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Based on the i387 version written by:
|
||||
* J.T. Conklin (jtc@wimsey.com), Winning Strategies, Inc.
|
||||
*/
|
||||
|
||||
#include <amd64/bsd_asm.h>
|
||||
|
||||
ENTRY(fmod)
|
||||
movsd %xmm0,-8(%rsp)
|
||||
movsd %xmm1,-16(%rsp)
|
||||
fldl -16(%rsp)
|
||||
fldl -8(%rsp)
|
||||
1: fprem
|
||||
fstsw %ax
|
||||
testw $0x400,%ax
|
||||
jne 1b
|
||||
fstpl -8(%rsp)
|
||||
movsd -8(%rsp),%xmm0
|
||||
fstp %st
|
||||
ret
|
||||
END(fmod)
|
||||
|
||||
/* Enable stack protection */
|
||||
#if defined(__ELF__)
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
#endif
|
26
src/openlibm/amd64/e_fmodf.S
Normal file
26
src/openlibm/amd64/e_fmodf.S
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Based on the i387 version written by J.T. Conklin <jtc@netbsd.org>.
|
||||
* Public domain.
|
||||
*/
|
||||
|
||||
#include <amd64/bsd_asm.h>
|
||||
|
||||
ENTRY(fmodf)
|
||||
movss %xmm0,-4(%rsp)
|
||||
movss %xmm1,-8(%rsp)
|
||||
flds -8(%rsp)
|
||||
flds -4(%rsp)
|
||||
1: fprem
|
||||
fstsw %ax
|
||||
testw $0x400,%ax
|
||||
jne 1b
|
||||
fstps -4(%rsp)
|
||||
movss -4(%rsp),%xmm0
|
||||
fstp %st
|
||||
ret
|
||||
END(fmodf)
|
||||
|
||||
/* Enable stack protection */
|
||||
#if defined(__ELF__)
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
#endif
|
52
src/openlibm/amd64/e_fmodl.S
Normal file
52
src/openlibm/amd64/e_fmodl.S
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (c) 1993,94 Winning Strategies, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must 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. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by Winning Strategies, Inc.
|
||||
* 4. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* 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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Based on the i387 version written by:
|
||||
* J.T. Conklin (jtc@wimsey.com), Winning Strategies, Inc.
|
||||
*/
|
||||
|
||||
#include <amd64/bsd_asm.h>
|
||||
|
||||
ENTRY(fmodl)
|
||||
fldt 24(%rsp)
|
||||
fldt 8(%rsp)
|
||||
1: fprem
|
||||
fstsw %ax
|
||||
testw $0x400,%ax
|
||||
jne 1b
|
||||
fstp %st(1)
|
||||
ret
|
||||
END(fmodl)
|
||||
|
||||
/* Enable stack protection */
|
||||
#if defined(__ELF__)
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
#endif
|
30
src/openlibm/amd64/e_remainder.S
Normal file
30
src/openlibm/amd64/e_remainder.S
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Based on the i387 version written by:
|
||||
* J.T. Conklin (jtc@netbsd.org)
|
||||
* Public domain.
|
||||
*/
|
||||
|
||||
#include <amd64/bsd_asm.h>
|
||||
|
||||
//RCSID("from: FreeBSD: src/lib/msun/i387/e_remainder.S,v 1.8 2005/02/04 14:08:32 das Exp")
|
||||
//__FBSDID("$FreeBSD: src/lib/msun/amd64/e_remainder.S,v 1.2 2011/01/07 16:13:12 kib Exp $")
|
||||
|
||||
ENTRY(remainder)
|
||||
movsd %xmm0,-8(%rsp)
|
||||
movsd %xmm1,-16(%rsp)
|
||||
fldl -16(%rsp)
|
||||
fldl -8(%rsp)
|
||||
1: fprem1
|
||||
fstsw %ax
|
||||
testw $0x400,%ax
|
||||
jne 1b
|
||||
fstpl -8(%rsp)
|
||||
movsd -8(%rsp),%xmm0
|
||||
fstp %st
|
||||
ret
|
||||
|
||||
|
||||
/* Enable stack protection */
|
||||
#if defined(__ELF__)
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
#endif
|
29
src/openlibm/amd64/e_remainderf.S
Normal file
29
src/openlibm/amd64/e_remainderf.S
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Based on the i387 version written by J.T. Conklin <jtc@netbsd.org>.
|
||||
* Public domain.
|
||||
*/
|
||||
|
||||
#include <amd64/bsd_asm.h>
|
||||
|
||||
//RCSID("from: $NetBSD: e_remainderf.S,v 1.2 1995/05/08 23:49:47 jtc Exp $")
|
||||
//__FBSDID("$FreeBSD: src/lib/msun/amd64/e_remainderf.S,v 1.2 2011/01/07 16:13:12 kib Exp $")
|
||||
|
||||
ENTRY(remainderf)
|
||||
movss %xmm0,-4(%rsp)
|
||||
movss %xmm1,-8(%rsp)
|
||||
flds -8(%rsp)
|
||||
flds -4(%rsp)
|
||||
1: fprem1
|
||||
fstsw %ax
|
||||
testw $0x400,%ax
|
||||
jne 1b
|
||||
fstps -4(%rsp)
|
||||
movss -4(%rsp),%xmm0
|
||||
fstp %st
|
||||
ret
|
||||
|
||||
|
||||
/* Enable stack protection */
|
||||
#if defined(__ELF__)
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
#endif
|
35
src/openlibm/amd64/e_remainderl.S
Normal file
35
src/openlibm/amd64/e_remainderl.S
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Based on the i387 version written by:
|
||||
* J.T. Conklin (jtc@netbsd.org)
|
||||
* Public domain.
|
||||
*/
|
||||
|
||||
#include <amd64/bsd_asm.h>
|
||||
|
||||
//__FBSDID("$FreeBSD: src/lib/msun/amd64/e_remainderl.S,v 1.2 2011/01/07 16:13:12 kib Exp $")
|
||||
|
||||
ENTRY(remainderl)
|
||||
#ifndef _WIN64
|
||||
fldt 24(%rsp)
|
||||
fldt 8(%rsp)
|
||||
#else
|
||||
fldt (%r8)
|
||||
fldt (%rdx)
|
||||
#endif
|
||||
1: fprem1
|
||||
fstsw %ax
|
||||
testw $0x400,%ax
|
||||
jne 1b
|
||||
fstp %st(1)
|
||||
#ifdef _WIN64
|
||||
mov %rcx,%rax
|
||||
movq $0x0,0x8(%rcx)
|
||||
fstpt (%rcx)
|
||||
#endif
|
||||
ret
|
||||
|
||||
|
||||
/* Enable stack protection */
|
||||
#if defined(__ELF__)
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
#endif
|
40
src/openlibm/amd64/e_sqrt.S
Normal file
40
src/openlibm/amd64/e_sqrt.S
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*-
|
||||
* Copyright (c) 2005 David Schultz <das@FreeBSD.ORG>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, 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 AUTHOR 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 AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (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 <amd64/bsd_asm.h>
|
||||
//__FBSDID("$FreeBSD: src/lib/msun/amd64/e_sqrt.S,v 1.4 2011/01/07 16:13:12 kib Exp $")
|
||||
|
||||
ENTRY(sqrt)
|
||||
sqrtsd %xmm0, %xmm0
|
||||
ret
|
||||
END(sqrt)
|
||||
|
||||
|
||||
|
||||
/* Enable stack protection */
|
||||
#if defined(__ELF__)
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
#endif
|
39
src/openlibm/amd64/e_sqrtf.S
Normal file
39
src/openlibm/amd64/e_sqrtf.S
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*-
|
||||
* Copyright (c) 2005 David Schultz <das@FreeBSD.ORG>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, 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 AUTHOR 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 AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (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 <amd64/bsd_asm.h>
|
||||
//__FBSDID("$FreeBSD: src/lib/msun/amd64/e_sqrtf.S,v 1.3 2011/01/07 16:13:12 kib Exp $")
|
||||
|
||||
ENTRY(sqrtf)
|
||||
sqrtss %xmm0, %xmm0
|
||||
ret
|
||||
END(sqrtf)
|
||||
|
||||
|
||||
/* Enable stack protection */
|
||||
#if defined(__ELF__)
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
#endif
|
46
src/openlibm/amd64/e_sqrtl.S
Normal file
46
src/openlibm/amd64/e_sqrtl.S
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*-
|
||||
* Copyright (c) 2008 David Schultz <das@FreeBSD.ORG>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, 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 AUTHOR 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 AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (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 <amd64/bsd_asm.h>
|
||||
//__FBSDID("$FreeBSD: src/lib/msun/amd64/e_sqrtl.S,v 1.2 2011/01/07 16:13:12 kib Exp $")
|
||||
|
||||
ENTRY(sqrtl)
|
||||
#ifndef _WIN64
|
||||
fldt 8(%rsp)
|
||||
fsqrt
|
||||
#else
|
||||
fldt (%rdx)
|
||||
fsqrt
|
||||
mov %rcx,%rax
|
||||
movq $0x0,0x8(%rcx)
|
||||
fstpt (%rcx)
|
||||
#endif
|
||||
ret
|
||||
|
||||
/* Enable stack protection */
|
||||
#if defined(__ELF__)
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
#endif
|
162
src/openlibm/amd64/fenv.c
Normal file
162
src/openlibm/amd64/fenv.c
Normal file
|
@ -0,0 +1,162 @@
|
|||
/*-
|
||||
* Copyright (c) 2004-2005 David Schultz <das@FreeBSD.ORG>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, 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 AUTHOR 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 AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD: src/lib/msun/amd64/fenv.c,v 1.8 2011/10/21 06:25:31 das Exp $
|
||||
*/
|
||||
|
||||
#include "bsd_fpu.h"
|
||||
#include "math_private.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define __fenv_static OLM_DLLEXPORT
|
||||
#endif
|
||||
#include <openlibm_fenv.h>
|
||||
|
||||
#ifdef __GNUC_GNU_INLINE__
|
||||
#error "This file must be compiled with C99 'inline' semantics"
|
||||
#endif
|
||||
|
||||
const fenv_t __fe_dfl_env = {
|
||||
{ 0xffff0000 | __INITIAL_FPUCW__,
|
||||
0xffff0000,
|
||||
0xffffffff,
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff }
|
||||
},
|
||||
__INITIAL_MXCSR__
|
||||
};
|
||||
|
||||
extern inline OLM_DLLEXPORT int feclearexcept(int __excepts);
|
||||
extern inline OLM_DLLEXPORT int fegetexceptflag(fexcept_t *__flagp, int __excepts);
|
||||
|
||||
OLM_DLLEXPORT int
|
||||
fesetexceptflag(const fexcept_t *flagp, int excepts)
|
||||
{
|
||||
fenv_t env;
|
||||
|
||||
__fnstenv(&env.__x87);
|
||||
env.__x87.__status &= ~excepts;
|
||||
env.__x87.__status |= *flagp & excepts;
|
||||
__fldenv(env.__x87);
|
||||
|
||||
__stmxcsr(&env.__mxcsr);
|
||||
env.__mxcsr &= ~excepts;
|
||||
env.__mxcsr |= *flagp & excepts;
|
||||
__ldmxcsr(env.__mxcsr);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
OLM_DLLEXPORT int
|
||||
feraiseexcept(int excepts)
|
||||
{
|
||||
fexcept_t ex = excepts;
|
||||
|
||||
fesetexceptflag(&ex, excepts);
|
||||
__fwait();
|
||||
return (0);
|
||||
}
|
||||
|
||||
extern inline OLM_DLLEXPORT int fetestexcept(int __excepts);
|
||||
extern inline OLM_DLLEXPORT int fegetround(void);
|
||||
extern inline OLM_DLLEXPORT int fesetround(int __round);
|
||||
|
||||
OLM_DLLEXPORT int
|
||||
fegetenv(fenv_t *envp)
|
||||
{
|
||||
|
||||
__fnstenv(&envp->__x87);
|
||||
__stmxcsr(&envp->__mxcsr);
|
||||
/*
|
||||
* fnstenv masks all exceptions, so we need to restore the
|
||||
* control word to avoid this side effect.
|
||||
*/
|
||||
__fldcw(envp->__x87.__control);
|
||||
return (0);
|
||||
}
|
||||
|
||||
OLM_DLLEXPORT int
|
||||
feholdexcept(fenv_t *envp)
|
||||
{
|
||||
uint32_t mxcsr;
|
||||
|
||||
__stmxcsr(&mxcsr);
|
||||
__fnstenv(&envp->__x87);
|
||||
__fnclex();
|
||||
envp->__mxcsr = mxcsr;
|
||||
mxcsr &= ~FE_ALL_EXCEPT;
|
||||
mxcsr |= FE_ALL_EXCEPT << _SSE_EMASK_SHIFT;
|
||||
__ldmxcsr(mxcsr);
|
||||
return (0);
|
||||
}
|
||||
|
||||
extern inline OLM_DLLEXPORT int fesetenv(const fenv_t *__envp);
|
||||
|
||||
OLM_DLLEXPORT int
|
||||
feupdateenv(const fenv_t *envp)
|
||||
{
|
||||
uint32_t mxcsr;
|
||||
uint16_t status;
|
||||
|
||||
__fnstsw(&status);
|
||||
__stmxcsr(&mxcsr);
|
||||
fesetenv(envp);
|
||||
feraiseexcept((mxcsr | status) & FE_ALL_EXCEPT);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
feenableexcept(int mask)
|
||||
{
|
||||
uint32_t mxcsr, omask;
|
||||
uint16_t control;
|
||||
|
||||
mask &= FE_ALL_EXCEPT;
|
||||
__fnstcw(&control);
|
||||
__stmxcsr(&mxcsr);
|
||||
omask = ~(control | mxcsr >> _SSE_EMASK_SHIFT) & FE_ALL_EXCEPT;
|
||||
control &= ~mask;
|
||||
__fldcw(control);
|
||||
mxcsr &= ~(mask << _SSE_EMASK_SHIFT);
|
||||
__ldmxcsr(mxcsr);
|
||||
return (omask);
|
||||
}
|
||||
|
||||
int
|
||||
fedisableexcept(int mask)
|
||||
{
|
||||
uint32_t mxcsr, omask;
|
||||
uint16_t control;
|
||||
|
||||
mask &= FE_ALL_EXCEPT;
|
||||
__fnstcw(&control);
|
||||
__stmxcsr(&mxcsr);
|
||||
omask = ~(control | mxcsr >> _SSE_EMASK_SHIFT) & FE_ALL_EXCEPT;
|
||||
control |= mask;
|
||||
__fldcw(control);
|
||||
mxcsr |= mask << _SSE_EMASK_SHIFT;
|
||||
__ldmxcsr(mxcsr);
|
||||
return (omask);
|
||||
}
|
12
src/openlibm/amd64/s_llrint.S
Normal file
12
src/openlibm/amd64/s_llrint.S
Normal file
|
@ -0,0 +1,12 @@
|
|||
#include <amd64/bsd_asm.h>
|
||||
//__FBSDID("$FreeBSD: src/lib/msun/amd64/s_llrint.S,v 1.3 2011/02/04 21:54:06 kib Exp $")
|
||||
|
||||
ENTRY(llrint)
|
||||
cvtsd2si %xmm0, %rax
|
||||
ret
|
||||
END(llrint)
|
||||
|
||||
/* Enable stack protection */
|
||||
#if defined(__ELF__)
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
#endif
|
12
src/openlibm/amd64/s_llrintf.S
Normal file
12
src/openlibm/amd64/s_llrintf.S
Normal file
|
@ -0,0 +1,12 @@
|
|||
#include <amd64/bsd_asm.h>
|
||||
//__FBSDID("$FreeBSD: src/lib/msun/amd64/s_llrintf.S,v 1.3 2011/02/04 21:54:06 kib Exp $")
|
||||
|
||||
ENTRY(llrintf)
|
||||
cvtss2si %xmm0, %rax
|
||||
ret
|
||||
END(llrintf)
|
||||
|
||||
/* Enable stack protection */
|
||||
#if defined(__ELF__)
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
#endif
|
45
src/openlibm/amd64/s_llrintl.S
Normal file
45
src/openlibm/amd64/s_llrintl.S
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*-
|
||||
* Copyright (c) 2005 David Schultz <das@FreeBSD.ORG>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, 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 AUTHOR 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 AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (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 <amd64/bsd_asm.h>
|
||||
//__FBSDID("$FreeBSD: src/lib/msun/amd64/s_llrintl.S,v 1.2 2011/01/07 16:13:12 kib Exp $");
|
||||
|
||||
ENTRY(llrintl)
|
||||
#ifndef _WIN64
|
||||
fldt 8(%rsp)
|
||||
#else
|
||||
fldt (%rcx)
|
||||
#endif
|
||||
subq $8,%rsp
|
||||
fistpll (%rsp)
|
||||
popq %rax
|
||||
ret
|
||||
|
||||
|
||||
/* Enable stack protection */
|
||||
#if defined(__ELF__)
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
#endif
|
29
src/openlibm/amd64/s_logbl.S
Normal file
29
src/openlibm/amd64/s_logbl.S
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Written by:
|
||||
* J.T. Conklin (jtc@netbsd.org)
|
||||
* Public domain.
|
||||
*/
|
||||
|
||||
#include <amd64/bsd_asm.h>
|
||||
//__FBSDID("$FreeBSD: src/lib/msun/amd64/s_logbl.S,v 1.4 2011/01/07 16:13:12 kib Exp $")
|
||||
|
||||
ENTRY(logbl)
|
||||
#ifndef _WIN64
|
||||
fldt 8(%rsp)
|
||||
#else
|
||||
fldt (%rdx)
|
||||
#endif
|
||||
fxtract
|
||||
fstp %st
|
||||
#ifdef _WIN64
|
||||
mov %rcx,%rax
|
||||
movq $0x0,0x8(%rcx)
|
||||
fstpt (%rcx)
|
||||
#endif
|
||||
ret
|
||||
|
||||
|
||||
/* Enable stack protection */
|
||||
#if defined(__ELF__)
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
#endif
|
44
src/openlibm/amd64/s_lrint.S
Normal file
44
src/openlibm/amd64/s_lrint.S
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*-
|
||||
* Copyright (c) 2005 David Schultz <das@FreeBSD.ORG>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, 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 AUTHOR 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 AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (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 <amd64/bsd_asm.h>
|
||||
|
||||
//__FBSDID("$FreeBSD: src/lib/msun/amd64/s_lrint.S,v 1.3 2011/01/07 16:13:12 kib Exp $")
|
||||
|
||||
ENTRY(lrint)
|
||||
#ifndef _WIN64
|
||||
cvtsd2si %xmm0, %rax
|
||||
#else
|
||||
cvtsd2si %xmm0, %eax
|
||||
#endif
|
||||
ret
|
||||
END(lrint)
|
||||
|
||||
|
||||
/* Enable stack protection */
|
||||
#if defined(__ELF__)
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue