2000-04-05 10:05:57 +04:00
|
|
|
// -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
|
|
//
|
|
|
|
// The contents of this file are subject to the Netscape Public
|
|
|
|
// License Version 1.1 (the "License"); you may not use this file
|
|
|
|
// except in compliance with the License. You may obtain a copy of
|
|
|
|
// the License at http://www.mozilla.org/NPL/
|
|
|
|
//
|
|
|
|
// Software distributed under the License is distributed on an "AS
|
|
|
|
// IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
|
|
|
|
// implied. See the License for the specific language governing
|
|
|
|
// rights and limitations under the License.
|
|
|
|
//
|
|
|
|
// The Original Code is the JavaScript 2 Prototype.
|
|
|
|
//
|
|
|
|
// The Initial Developer of the Original Code is Netscape
|
|
|
|
// Communications Corporation. Portions created by Netscape are
|
|
|
|
// Copyright (C) 1998 Netscape Communications Corporation. All
|
|
|
|
// Rights Reserved.
|
|
|
|
|
|
|
|
#include "interpreter.h"
|
2000-04-06 00:33:41 +04:00
|
|
|
#include "world.h"
|
|
|
|
|
|
|
|
#include <map>
|
2000-04-05 10:05:57 +04:00
|
|
|
|
2000-04-05 10:10:53 +04:00
|
|
|
namespace JavaScript {
|
2000-04-05 10:05:57 +04:00
|
|
|
|
2000-04-07 08:39:50 +04:00
|
|
|
using std::map;
|
|
|
|
using std::less;
|
|
|
|
using std::pair;
|
2000-04-05 10:05:57 +04:00
|
|
|
|
2000-04-07 06:52:07 +04:00
|
|
|
/**
|
|
|
|
* Private representation of a JavaScript object.
|
|
|
|
* This will change over time, so it is treated as an opaque
|
|
|
|
* type everywhere else but here.
|
|
|
|
*/
|
2000-04-08 04:52:59 +04:00
|
|
|
|
|
|
|
#if defined(XP_MAC)
|
|
|
|
// copied from default template parameters in map.
|
|
|
|
typedef gc_allocator<pair<const String, JSValue> > gc_map_allocator;
|
|
|
|
#elif defined(XP_UNIX)
|
|
|
|
// FIXME: in libg++, they assume the map's allocator is a byte allocator,
|
|
|
|
// which is wrapped in a simple_allocator. this is crap.
|
|
|
|
typedef char _Char[1];
|
|
|
|
typedef gc_allocator<_Char> gc_map_allocator;
|
|
|
|
#elif defined(_WIN32)
|
|
|
|
// FIXME: MSVC++'s notion. this is why we had to add _Charalloc().
|
|
|
|
typedef gc_allocator<JSValue> gc_map_allocator;
|
2000-04-08 02:19:36 +04:00
|
|
|
#endif
|
2000-04-08 04:52:59 +04:00
|
|
|
|
|
|
|
class JSObject : public map<String, JSValue, less<String>, gc_map_allocator> {
|
2000-04-07 06:52:07 +04:00
|
|
|
public:
|
2000-04-08 02:19:36 +04:00
|
|
|
void* operator new(size_t) { return alloc.allocate(1, 0); }
|
2000-04-07 08:39:50 +04:00
|
|
|
void operator delete(void* /* ptr */) {}
|
|
|
|
private:
|
|
|
|
static gc_allocator<JSObject> alloc;
|
2000-04-07 06:52:07 +04:00
|
|
|
};
|
|
|
|
|
2000-04-07 08:39:50 +04:00
|
|
|
// static allocator (required when gc_allocator<T> is allocator<T>.
|
|
|
|
gc_allocator<JSObject> JSObject::alloc;
|
|
|
|
|
|
|
|
// operand access macros.
|
|
|
|
#define op1(i) (i->itsOperand1)
|
|
|
|
#define op2(i) (i->itsOperand2)
|
|
|
|
#define op3(i) (i->itsOperand3)
|
|
|
|
|
2000-04-08 05:04:55 +04:00
|
|
|
JSValue interpret(ICodeModule *iCode, const JSValues& args)
|
2000-04-05 10:05:57 +04:00
|
|
|
{
|
2000-04-06 00:33:41 +04:00
|
|
|
JSValue result;
|
2000-04-05 10:05:57 +04:00
|
|
|
JSValues frame(args);
|
2000-04-08 05:04:55 +04:00
|
|
|
JSValues registers(iCode->itsMaxRegister + 1);
|
2000-04-07 06:52:07 +04:00
|
|
|
static JSObject globals;
|
2000-04-05 10:05:57 +04:00
|
|
|
|
2000-04-08 05:04:55 +04:00
|
|
|
InstructionIterator begin_pc = iCode->its_iCode->begin();
|
2000-04-08 05:08:01 +04:00
|
|
|
InstructionIterator end_pc = iCode->its_iCode->end();
|
|
|
|
InstructionIterator pc = begin_pc;
|
2000-04-08 05:04:55 +04:00
|
|
|
while (pc != end_pc) {
|
2000-04-05 10:05:57 +04:00
|
|
|
Instruction* instruction = *pc;
|
2000-04-06 00:33:41 +04:00
|
|
|
switch (instruction->opcode()) {
|
2000-04-06 06:58:22 +04:00
|
|
|
case MOVE_TO:
|
|
|
|
{
|
2000-04-07 06:52:07 +04:00
|
|
|
Move* mov = static_cast<Move*>(instruction);
|
2000-04-08 02:19:36 +04:00
|
|
|
registers[op1(mov)] = registers[op2(mov)];
|
2000-04-06 06:58:22 +04:00
|
|
|
}
|
|
|
|
break;
|
2000-04-08 04:52:59 +04:00
|
|
|
case LOAD_NAME:
|
2000-04-05 10:05:57 +04:00
|
|
|
{
|
2000-04-07 06:52:07 +04:00
|
|
|
LoadName* ln = static_cast<LoadName*>(instruction);
|
2000-04-08 04:52:59 +04:00
|
|
|
registers[op1(ln)] = globals[*op2(ln)];
|
2000-04-06 00:33:41 +04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SAVE_NAME:
|
|
|
|
{
|
2000-04-07 06:52:07 +04:00
|
|
|
SaveName* sn = static_cast<SaveName*>(instruction);
|
|
|
|
globals[*op1(sn)] = registers[op2(sn)];
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case NEW_OBJECT:
|
2000-04-08 04:52:59 +04:00
|
|
|
{
|
|
|
|
NewObject* no = static_cast<NewObject*>(instruction);
|
|
|
|
registers[op1(no)].obj = new JSObject();
|
|
|
|
}
|
|
|
|
break;
|
2000-04-07 06:52:07 +04:00
|
|
|
case GET_PROP:
|
2000-04-08 04:52:59 +04:00
|
|
|
{
|
|
|
|
GetProp* gp = static_cast<GetProp*>(instruction);
|
|
|
|
JSObject* obj = registers[op2(gp)].obj;
|
|
|
|
registers[op1(gp)] = (*obj)[*op3(gp)];
|
|
|
|
}
|
|
|
|
break;
|
2000-04-07 06:52:07 +04:00
|
|
|
case SET_PROP:
|
2000-04-08 04:52:59 +04:00
|
|
|
{
|
|
|
|
SetProp* sp = static_cast<SetProp*>(instruction);
|
|
|
|
JSObject* obj = registers[op2(sp)].obj;
|
|
|
|
(*obj)[*op1(sp)] = registers[op3(sp)];
|
|
|
|
}
|
|
|
|
break;
|
2000-04-06 00:33:41 +04:00
|
|
|
case LOAD_IMMEDIATE:
|
2000-04-05 10:05:57 +04:00
|
|
|
{
|
2000-04-07 06:52:07 +04:00
|
|
|
LoadImmediate* li = static_cast<LoadImmediate*>(instruction);
|
2000-04-08 02:19:36 +04:00
|
|
|
registers[op1(li)] = JSValue(op2(li));
|
2000-04-05 10:05:57 +04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case LOAD_VAR:
|
|
|
|
{
|
2000-04-07 06:52:07 +04:00
|
|
|
LoadVar* lv = static_cast<LoadVar*>(instruction);
|
2000-04-08 02:19:36 +04:00
|
|
|
registers[op1(lv)] = frame[op2(lv)];
|
2000-04-05 10:05:57 +04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SAVE_VAR:
|
|
|
|
{
|
2000-04-07 06:52:07 +04:00
|
|
|
SaveVar* sv = static_cast<SaveVar*>(instruction);
|
|
|
|
frame[op1(sv)] = registers[op2(sv)];
|
2000-04-05 10:05:57 +04:00
|
|
|
}
|
|
|
|
break;
|
2000-04-06 00:33:41 +04:00
|
|
|
case BRANCH:
|
2000-04-05 10:05:57 +04:00
|
|
|
{
|
2000-04-08 02:19:36 +04:00
|
|
|
ResolvedBranch* bra = static_cast<ResolvedBranch*>(instruction);
|
2000-04-08 05:04:55 +04:00
|
|
|
pc = begin_pc + op1(bra);
|
2000-04-06 06:58:22 +04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case BRANCH_LT:
|
|
|
|
{
|
2000-04-08 02:19:36 +04:00
|
|
|
ResolvedBranchCond* bc = static_cast<ResolvedBranchCond*>(instruction);
|
2000-04-07 06:52:07 +04:00
|
|
|
if (registers[op2(bc)].i32 < 0) {
|
2000-04-08 05:04:55 +04:00
|
|
|
pc = begin_pc + op1(bc);
|
2000-04-06 06:58:22 +04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case BRANCH_LE:
|
|
|
|
{
|
2000-04-08 02:19:36 +04:00
|
|
|
ResolvedBranchCond* bc = static_cast<ResolvedBranchCond*>(instruction);
|
2000-04-07 06:52:07 +04:00
|
|
|
if (registers[op2(bc)].i32 <= 0) {
|
2000-04-08 05:04:55 +04:00
|
|
|
pc = begin_pc + op1(bc);
|
2000-04-06 06:58:22 +04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case BRANCH_EQ:
|
|
|
|
{
|
2000-04-08 02:19:36 +04:00
|
|
|
ResolvedBranchCond* bc = static_cast<ResolvedBranchCond*>(instruction);
|
2000-04-07 06:52:07 +04:00
|
|
|
if (registers[op2(bc)].i32 == 0) {
|
2000-04-08 05:04:55 +04:00
|
|
|
pc = begin_pc + op1(bc);
|
2000-04-06 06:58:22 +04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case BRANCH_NE:
|
|
|
|
{
|
2000-04-08 02:19:36 +04:00
|
|
|
ResolvedBranchCond* bc = static_cast<ResolvedBranchCond*>(instruction);
|
2000-04-07 06:52:07 +04:00
|
|
|
if (registers[op2(bc)].i32 != 0) {
|
2000-04-08 05:04:55 +04:00
|
|
|
pc = begin_pc + op1(bc);
|
2000-04-06 06:58:22 +04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case BRANCH_GE:
|
|
|
|
{
|
2000-04-08 02:19:36 +04:00
|
|
|
ResolvedBranchCond* bc = static_cast<ResolvedBranchCond*>(instruction);
|
2000-04-07 06:52:07 +04:00
|
|
|
if (registers[op2(bc)].i32 >= 0) {
|
2000-04-08 05:04:55 +04:00
|
|
|
pc = begin_pc + op1(bc);
|
2000-04-06 06:58:22 +04:00
|
|
|
continue;
|
|
|
|
}
|
2000-04-05 10:05:57 +04:00
|
|
|
}
|
|
|
|
break;
|
2000-04-06 06:58:22 +04:00
|
|
|
case BRANCH_GT:
|
2000-04-05 10:05:57 +04:00
|
|
|
{
|
2000-04-08 02:19:36 +04:00
|
|
|
ResolvedBranchCond* bc = static_cast<ResolvedBranchCond*>(instruction);
|
2000-04-07 06:52:07 +04:00
|
|
|
if (registers[op2(bc)].i32 > 0) {
|
2000-04-08 05:04:55 +04:00
|
|
|
pc = begin_pc + op1(bc);
|
2000-04-06 06:58:22 +04:00
|
|
|
continue;
|
|
|
|
}
|
2000-04-05 10:05:57 +04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ADD:
|
|
|
|
{
|
|
|
|
// could get clever here with Functional forms.
|
2000-04-07 06:52:07 +04:00
|
|
|
Arithmetic* add = static_cast<Arithmetic*>(instruction);
|
2000-04-08 02:19:36 +04:00
|
|
|
registers[op1(add)] = JSValue(registers[op2(add)].f64 + registers[op3(add)].f64);
|
2000-04-05 10:05:57 +04:00
|
|
|
}
|
|
|
|
break;
|
2000-04-06 06:58:22 +04:00
|
|
|
case SUBTRACT:
|
2000-04-05 10:05:57 +04:00
|
|
|
{
|
2000-04-07 06:52:07 +04:00
|
|
|
Arithmetic* sub = static_cast<Arithmetic*>(instruction);
|
2000-04-08 02:19:36 +04:00
|
|
|
registers[op1(sub)] = JSValue(registers[op2(sub)].f64 - registers[op3(sub)].f64);
|
2000-04-05 10:05:57 +04:00
|
|
|
}
|
|
|
|
break;
|
2000-04-06 06:58:22 +04:00
|
|
|
case MULTIPLY:
|
2000-04-05 10:05:57 +04:00
|
|
|
{
|
2000-04-07 06:52:07 +04:00
|
|
|
Arithmetic* mul = static_cast<Arithmetic*>(instruction);
|
2000-04-08 02:19:36 +04:00
|
|
|
registers[op1(mul)] = JSValue(registers[op2(mul)].f64 * registers[op3(mul)].f64);
|
2000-04-06 06:58:22 +04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case DIVIDE:
|
|
|
|
{
|
2000-04-07 06:52:07 +04:00
|
|
|
Arithmetic* div = static_cast<Arithmetic*>(instruction);
|
2000-04-08 02:19:36 +04:00
|
|
|
registers[op1(div)] = JSValue(registers[op2(div)].f64 / registers[op3(div)].f64);
|
2000-04-06 06:58:22 +04:00
|
|
|
}
|
|
|
|
break;
|
2000-04-07 02:40:17 +04:00
|
|
|
case COMPARE_LT:
|
|
|
|
case COMPARE_LE:
|
|
|
|
case COMPARE_EQ:
|
|
|
|
case COMPARE_NE:
|
|
|
|
case COMPARE_GT:
|
|
|
|
case COMPARE_GE:
|
2000-04-06 06:58:22 +04:00
|
|
|
{
|
2000-04-07 06:52:07 +04:00
|
|
|
Arithmetic* cmp = static_cast<Arithmetic*>(instruction);
|
2000-04-08 02:19:36 +04:00
|
|
|
float64 diff = (registers[op2(cmp)].f64 - registers[op3(cmp)].f64);
|
|
|
|
registers[op1(cmp)].i32 = (diff == 0.0 ? 0 : (diff > 0.0 ? 1 : -1));
|
2000-04-05 10:05:57 +04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case NOT:
|
|
|
|
{
|
2000-04-07 06:52:07 +04:00
|
|
|
Move* nt = static_cast<Move*>(instruction);
|
2000-04-08 02:19:36 +04:00
|
|
|
registers[op1(nt)].i32 = !registers[op2(nt)].i32;
|
|
|
|
}
|
|
|
|
break;
|
2000-04-08 04:52:59 +04:00
|
|
|
case RETURN:
|
|
|
|
{
|
|
|
|
Return* ret = static_cast<Return*>(instruction);
|
|
|
|
result = registers[op1(ret)];
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
2000-04-05 10:05:57 +04:00
|
|
|
}
|
2000-04-06 06:58:22 +04:00
|
|
|
|
|
|
|
// increment the program counter.
|
|
|
|
++pc;
|
2000-04-05 10:05:57 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|