Add files to repository

This commit is contained in:
Alec Murphy 2025-06-09 20:06:04 -04:00
parent 9f200c9084
commit 23d704d496
542 changed files with 75775 additions and 0 deletions

14
.clang-format Normal file
View 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
View 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
View 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
View 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
View 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
View 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 [&param0], 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
View 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
View 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
View 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
View 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
View 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
View 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
View file

@ -0,0 +1,4 @@
[Project]
CreatedFrom=
Manager=KDevCustomBuildSystem
Name=mujs

152
scripts/build-all Executable file
View 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
View 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
View 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
View 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
View file

@ -0,0 +1 @@
Tor Andersson <tor.andersson@artifex.com>

16
src/mujs/COPYING Normal file
View 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
View 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
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

224
src/mujs/docs/examples.html Normal file
View 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 &lt;stdio.h&gt;
#include &lt;mujs.h&gt;
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 &lt;stdio.h&gt;
#include &lt;mujs.h&gt;
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 &lt;stdio.h&gt;
#include &lt;mujs.h&gt;
#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 &copy; 2013-2017 Artifex Software Inc.
</footer>
</body>
</html>

58
src/mujs/docs/index.html Normal file
View 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 &copy; 2013-2017 Artifex Software Inc.
</footer>
</body>
</html>

View 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 &mdash; 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 &copy; 2013-2017 Artifex Software Inc.
</footer>
</body>
</html>

View 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 &copy; 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 &copy; 2013-2017 Artifex Software Inc.
</footer>
</body>
</html>

47
src/mujs/docs/logo.ps Normal file
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

View 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 &copy; 2013-2017 Artifex Software Inc.
</footer>
</body>
</html>

40
src/mujs/docs/style.css Normal file
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

861
src/mujs/jsdate.c Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

341
src/mujs/jsproperty.c Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

334
src/mujs/jsstate.c Normal file
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

85
src/mujs/opnames.h Normal file
View 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
View 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

File diff suppressed because it is too large Load diff

46
src/mujs/regexp.h Normal file
View 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
View 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 ]
)

View 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
View 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
View 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

File diff suppressed because it is too large Load diff

7
src/openlibm/.github/dependabot.yml vendored Normal file
View 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
View 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/

View 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

View 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
View 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
View 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
View 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
View 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
View 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
View file

@ -0,0 +1,71 @@
# OpenLibm
[![codecov](https://codecov.io/gh/JuliaMath/openlibm/graph/badge.svg?token=eTAdN7d9cg)](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.

View file

@ -0,0 +1 @@
$(CUR_SRCS) = fenv.c

View 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);

View 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

View 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_ */

View 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_ */

View 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_ */

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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
View 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);
}

View 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

View 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

View 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

View 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

View 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