Compare commits

..

2 Commits

Author SHA1 Message Date
fur%netscape.com
1c43d4984f This is a copy of regalloc_code2_BRANCH from Netscape's private repository,
as it existed in January of 1998.


git-svn-id: svn://10.0.0.236/branches/regalloc_code2_BRANCH@22571 18797224-902f-48f8-a5cc-f745e15eee43
1999-03-02 16:12:08 +00:00
(no author)
cfe021ff88 This commit was manufactured by cvs2svn to create branch
'regalloc_code2_BRANCH'.

git-svn-id: svn://10.0.0.236/branches/regalloc_code2_BRANCH@22567 18797224-902f-48f8-a5cc-f745e15eee43
1999-03-02 15:57:58 +00:00
207 changed files with 5176 additions and 65527 deletions

View File

@@ -0,0 +1,134 @@
/* -*- 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "Fundamentals.h"
#include "BitSet.h"
// Return the next bit after index set to true or -1 if none.
//
Int32 BitSet::nextOne(Int32 pos) const
{
++pos;
if (pos < 0 || Uint32(pos) >= universeSize)
return -1;
Uint32 offset = getWordOffset(pos);
Uint8 index = getBitOffset(pos);
Word* ptr = &word[offset];
Word currentWord = *ptr++ >> index;
if (currentWord != Word(0)) {
while ((currentWord & Word(1)) == 0) {
++index;
currentWord >>= 1;
}
return (offset << nBitsInWordLog2) + index;
}
Word* limit = &word[getSizeInWords(universeSize)];
while (ptr < limit) {
++offset;
currentWord = *ptr++;
if (currentWord != Word(0)) {
index = 0;
while ((currentWord & Word(1)) == 0) {
++index;
currentWord >>= 1;
}
return (offset << nBitsInWordLog2) + index;
}
}
return -1;
}
// Return the next bit after index set to false or -1 if none.
//
Int32 BitSet::nextZero(Int32 pos) const
{
++pos;
if (pos < 0 || Uint32(pos) >= universeSize)
return -1;
Uint32 offset = getWordOffset(pos);
Uint8 index = getBitOffset(pos);
Word* ptr = &word[offset];
Word currentWord = *ptr++ >> index;
if (currentWord != Word(~0)) {
for (; index < nBitsInWord; ++index) {
if ((currentWord & Word(1)) == 0) {
Int32 ret = (offset << nBitsInWordLog2) + index;
return (Uint32(ret) < universeSize) ? ret : -1;
}
currentWord >>= 1;
}
}
Word* limit = &word[getSizeInWords(universeSize)];
while (ptr < limit) {
++offset;
currentWord = *ptr++;
if (currentWord != Word(~0)) {
for (index = 0; index < nBitsInWord; ++index) {
if ((currentWord & Word(1)) == 0) {
Int32 ret = (offset << nBitsInWordLog2) + index;
return (Uint32(ret) < universeSize) ? ret : -1;
}
currentWord >>= 1;
}
}
}
return -1;
}
#ifdef DEBUG_LOG
// Print the set.
//
void BitSet::printPretty(LogModuleObject log)
{
UT_OBJECTLOG(log, PR_LOG_ALWAYS, ("[ "));
for (Int32 i = firstOne(); i != -1; i = nextOne(i)) {
Int32 currentBit = i;
UT_OBJECTLOG(log, PR_LOG_ALWAYS, ("%d", currentBit));
Int32 nextBit = nextOne(currentBit);
if (nextBit != currentBit + 1) {
UT_OBJECTLOG(log, PR_LOG_ALWAYS, (" "));
continue;
}
while ((nextBit != -1) && (nextBit == (currentBit + 1))) {
currentBit = nextBit;
nextBit = nextOne(nextBit);
}
if (currentBit > (i+1))
UT_OBJECTLOG(log, PR_LOG_ALWAYS, ("-%d ", currentBit));
else
UT_OBJECTLOG(log, PR_LOG_ALWAYS, (" %d ", currentBit));
i = currentBit;
}
UT_OBJECTLOG(log, PR_LOG_ALWAYS, ("]\n"));
}
#endif // DEBUG_LOG

View File

@@ -0,0 +1,195 @@
/* -*- 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#ifndef _BITSET_H_
#define _BITSET_H_
#include "Fundamentals.h"
#include "LogModule.h"
#include "Pool.h"
#include <string.h>
//------------------------------------------------------------------------------
// BitSet -
class BitSet
{
private:
#if (PR_BITS_PER_WORD == 64)
typedef Uint64 Word;
#elif (PR_BITS_PER_WORD == 32)
typedef Uint32 Word;
#endif
static const nBitsInWord = PR_BITS_PER_WORD;
static const nBytesInWord = PR_BYTES_PER_WORD;
static const nBitsInWordLog2 = PR_BITS_PER_WORD_LOG2;
static const nBytesInWordLog2 = PR_BYTES_PER_WORD_LOG2;
// Return the number of Word need to store the universe.
static Uint32 getSizeInWords(Uint32 sizeOfUniverse) {return (sizeOfUniverse + (nBitsInWord - 1)) >> nBitsInWordLog2;}
// Return the given element offset in its containing Word.
static Uint32 getBitOffset(Uint32 element) {return element & (nBitsInWord - 1);}
// Return the Word offset for the given element int the universe.
static Uint32 getWordOffset(Uint32 element) {return element >> nBitsInWordLog2;}
// Return the mask for the given bit index.
static Word getMask(Uint8 index) {return Word(1) << index;}
private:
Uint32 universeSize; // Size of the universe
Word* word; // universe memory.
private:
// No copy constructor.
BitSet(const BitSet&);
// Check if the given set's universe is of the same size than this universe.
void checkUniverseCompatibility(const BitSet& set) const {assert(set.universeSize == universeSize);}
// Check if pos is valid for this set's universe.
void checkMember(Int32 pos) const {assert(pos >=0 && Uint32(pos) < universeSize);}
public:
// Create a bitset of universeSize bits.
BitSet(Pool& pool, Uint32 universeSize) : universeSize(universeSize) {word = new(pool) Word[getSizeInWords(universeSize)]; clear();}
// Return the size of this bitset.
Uint32 getSize() const {return universeSize;}
// Clear the bitset.
void clear() {memset(word, 0x00, getSizeInWords(universeSize) << nBytesInWordLog2);}
// Clear the bit at index.
void clear(Uint32 index) {checkMember(index); word[getWordOffset(index)] &= ~getMask(index);}
// Set the bitset.
void set() {memset(word, 0xFF, getSizeInWords(universeSize) << nBytesInWordLog2);}
// Set the bit at index.
void set(Uint32 index) {checkMember(index); word[getWordOffset(index)] |= getMask(index);}
// Return true if the bit at index is set.
bool test(Uint32 index) const {checkMember(index); return (word[getWordOffset(index)] & getMask(index)) != 0;}
// Union with the given bitset.
inline void or(const BitSet& set);
// Intersection with the given bitset.
inline void and(const BitSet& set);
// Difference with the given bitset.
inline void difference(const BitSet& set);
// Copy set.
inline BitSet& operator = (const BitSet& set);
// Return true if the bitset are identical.
friend bool operator == (const BitSet& set1, const BitSet& set2);
// Return true if the bitset are different.
friend bool operator != (const BitSet& set1, const BitSet& set2);
// Logical operators.
BitSet& operator |= (const BitSet& set) {or(set); return *this;}
BitSet& operator &= (const BitSet& set) {and(set); return *this;}
BitSet& operator -= (const BitSet& set) {difference(set); return *this;}
// Return the first bit at set to true or -1 if none.
Int32 firstOne() const {return nextOne(-1);}
// Return the next bit after index set to true or -1 if none.
Int32 nextOne(Int32 pos) const;
// Return the first bit at set to false or -1 if none.
Int32 firstZero() const {return nextZero(-1);}
// Return the next bit after index set to false or -1 if none.
Int32 nextZero(Int32 pos) const;
// Iterator to conform with the set API.
typedef Int32 iterator;
// Return true if the walk is ordered.
static bool isOrdered() {return true;}
// Return the iterator for the first element of this set.
iterator begin() const {return firstOne();}
// Return the next iterator.
iterator advance(iterator pos) const {return nextOne(pos);}
// Return true if the iterator is at the end of the set.
bool done(iterator pos) const {return pos == -1;}
// Return the element corresponding to the given iterator.
Uint32 get(iterator pos) const {return pos;}
#ifdef DEBUG_LOG
// Print the set.
void printPretty(LogModuleObject log);
#endif // DEBUG_LOG
};
// Union with the given bitset.
//
inline void BitSet::or(const BitSet& set)
{
checkUniverseCompatibility(set);
Word* src = set.word;
Word* dst = word;
Word* limit = &src[getSizeInWords(universeSize)];
while (src < limit)
*dst++ |= *src++;
}
// Intersection with the given bitset.
//
inline void BitSet::and(const BitSet& set)
{
checkUniverseCompatibility(set);
Word* src = set.word;
Word* dst = word;
Word* limit = &src[getSizeInWords(universeSize)];
while (src < limit)
*dst++ &= *src++;
}
// Difference with the given bitset.
//
inline void BitSet::difference(const BitSet& set)
{
checkUniverseCompatibility(set);
Word* src = set.word;
Word* dst = word;
Word* limit = &src[getSizeInWords(universeSize)];
while (src < limit)
*dst++ &= ~*src++;
}
// Copy the given set into this set.
//
inline BitSet& BitSet::operator = (const BitSet& set)
{
checkUniverseCompatibility(set);
if (this != &set)
memcpy(word, set.word, getSizeInWords(universeSize) << nBytesInWordLog2);
return *this;
}
// Return true if the given set is identical to this set.
inline bool operator == (const BitSet& set1, const BitSet& set2)
{
set1.checkUniverseCompatibility(set2);
if (&set1 == &set2)
return true;
return memcmp(set1.word, set2.word, BitSet::getSizeInWords(set1.universeSize) << BitSet::nBytesInWordLog2) == 0;
}
inline bool operator != (const BitSet& set1, const BitSet& set2) {return !(set1 == set2);}
#endif // _BITSET_H

View File

@@ -0,0 +1,159 @@
/* -*- 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#ifndef _COALESCING_H_
#define _COALESCING_H_
#include "Fundamentals.h"
#include "Pool.h"
#include "RegisterPressure.h"
#include "InterferenceGraph.h"
#include "ControlGraph.h"
#include "ControlNodes.h"
#include "Instruction.h"
#include "SparseSet.h"
#include "RegisterAllocator.h"
#include "RegisterAllocatorTools.h"
#if 1
// Performing an ultra conservative coalescing meens that when we look at
// candidates (source,destination) for coalescing we need to make sure
// that the combined interference of the source and destination register
// will not exceed the total number of register available for the register
// class.
#define ULTRA_CONSERVATIVE_COALESCING
#else
// If we are not doing an ultra conservative coalescing we have to make sure
// that the total number of neighbor whose degree is greater than the total
// number of register is not greater than the total number of register.
#undef ULTRA_CONSERVATIVE_COALESCING
#endif
template <class RegisterPressure>
struct Coalescing
{
static bool coalesce(RegisterAllocator& registerAllocator);
};
template <class RegisterPressure>
bool Coalescing<RegisterPressure>::coalesce(RegisterAllocator& registerAllocator)
{
Pool& pool = registerAllocator.pool;
// Initialize the lookup table
//
Uint32 rangeCount = registerAllocator.rangeCount;
RegisterName* newRange = new RegisterName[2 * rangeCount];
RegisterName* coalescedRange = &newRange[rangeCount];
RegisterName* name2range = registerAllocator.name2range;
init(coalescedRange, rangeCount);
SparseSet interferences(pool, rangeCount);
InterferenceGraph<RegisterPressure>& iGraph = registerAllocator.iGraph;
bool removedInstructions = false;
ControlGraph& controlGraph = registerAllocator.controlGraph;
ControlNode** nodes = controlGraph.lndList;
Uint32 nNodes = controlGraph.nNodes;
// Walk the nodes in the loop nesting depth list.
for (Int32 n = nNodes - 1; n >= 0; n--) {
InstructionList& instructions = nodes[n]->getInstructions();
InstructionList::iterator it = instructions.begin();
while (!instructions.done(it)) {
Instruction& instruction = instructions.get(it);
it = instructions.advance(it);
if ((instruction.getFlags() & ifCopy) != 0) {
assert(instruction.getInstructionUseBegin() != instruction.getInstructionUseEnd() && instruction.getInstructionUseBegin()[0].isRegister());
assert(instruction.getInstructionDefineBegin() != instruction.getInstructionDefineEnd() && instruction.getInstructionDefineBegin()[0].isRegister());
RegisterName source = findRoot(name2range[instruction.getInstructionUseBegin()[0].getRegisterName()], coalescedRange);
RegisterName destination = findRoot(name2range[instruction.getInstructionDefineBegin()[0].getRegisterName()], coalescedRange);
if (source == destination) {
instruction.remove();
} else if (!iGraph.interfere(source, destination)) {
InterferenceVector* sourceVector = iGraph.getInterferenceVector(source);
InterferenceVector* destinationVector = iGraph.getInterferenceVector(destination);
#ifdef ULTRA_CONSERVATIVE_COALESCING
interferences.clear();
InterferenceVector* vector;
for (vector = sourceVector; vector != NULL; vector = vector->next) {
RegisterName* neighbors = vector->neighbors;
for (Uint32 i = 0; i < vector->count; i++)
interferences.set(findRoot(neighbors[i], coalescedRange));
}
for (vector = destinationVector; vector != NULL; vector = vector->next) {
RegisterName* neighbors = vector->neighbors;
for (Uint32 i = 0; i < vector->count; i++)
interferences.set(findRoot(neighbors[i], coalescedRange));
}
Uint32 count = interferences.getSize();
#else // ULTRA_CONSERVATIVE_COALESCING
trespass("not implemented");
Uint32 count = 0;
#endif // ULTRA_CONSERVATIVE_COALESCING
if (count < 6 /* FIX: should get the number from the class */) {
// Update the interferences vector.
if (sourceVector == NULL) {
iGraph.setInterferenceVector(source, destinationVector);
sourceVector = destinationVector;
} else if (destinationVector == NULL)
iGraph.setInterferenceVector(destination, sourceVector);
else {
InterferenceVector* last = NULL;
for (InterferenceVector* v = sourceVector; v != NULL; v = v->next)
last = v;
assert(last);
last->next = destinationVector;
iGraph.setInterferenceVector(destination, sourceVector);
}
// Update the interference matrix.
for (InterferenceVector* v = sourceVector; v != NULL; v = v->next) {
RegisterName* neighbors = v->neighbors;
for (Uint32 i = 0; i < v->count; i++) {
RegisterName neighbor = findRoot(neighbors[i], coalescedRange);
iGraph.setInterference(neighbor, source);
iGraph.setInterference(neighbor, destination);
}
}
instruction.remove();
coalescedRange[source] = destination;
removedInstructions = true;
}
}
}
}
}
registerAllocator.rangeCount = compress(registerAllocator.name2range, coalescedRange, registerAllocator.nameCount, rangeCount);
delete newRange;
return removedInstructions;
}
#endif // _COALESCING_H_

View File

@@ -0,0 +1,283 @@
/* -*- 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#ifndef NEW_LAURENTM_CODE
#include "Coloring.h"
#include "VirtualRegister.h"
#include "FastBitSet.h"
#include "FastBitMatrix.h"
#include "CpuInfo.h"
bool Coloring::
assignRegisters(FastBitMatrix& interferenceMatrix)
{
PRUint32 *stackPtr = new(pool) PRUint32[vRegManager.count()];
return select(interferenceMatrix, stackPtr, simplify(interferenceMatrix, stackPtr));
}
PRInt32 Coloring::
getLowestSpillCostRegister(FastBitSet& bitset)
{
PRInt32 lowest = bitset.firstOne();
if (lowest != -1)
{
Flt32 cost = vRegManager.getVirtualRegister(lowest).spillInfo.spillCost;
for (PRInt32 r = bitset.nextOne(lowest); r != -1; r = bitset.nextOne(r))
{
VirtualRegister& vReg = vRegManager.getVirtualRegister(r);
if (!vReg.spillInfo.infiniteSpillCost && (vReg.spillInfo.spillCost < cost))
{
cost = vReg.spillInfo.spillCost;
lowest = r;
}
}
}
return lowest;
}
PRUint32* Coloring::
simplify(FastBitMatrix interferenceMatrix, PRUint32* stackPtr)
{
// first we construct the sets low and high. low contains all nodes of degree
// inferior to the number of register available on the processor. All the
// nodes with an high degree and a finite spill cost are placed in high.
// Nodes of high degree and infinite spill cost are not included in either sets.
PRUint32 nRegisters = vRegManager.count();
FastBitSet low(pool, nRegisters);
FastBitSet high(pool, nRegisters);
FastBitSet stack(pool, nRegisters);
for (VirtualRegisterManager::iterator i = vRegManager.begin(); !vRegManager.done(i); i = vRegManager.advance(i))
{
VirtualRegister& vReg = vRegManager.getVirtualRegister(i);
if (vReg.getClass() == vrcStackSlot)
{
stack.set(i);
vReg.colorRegister(nRegisters);
}
else
{
if (vReg.colorInfo.interferenceDegree < NUMBER_OF_REGISTERS)
low.set(i);
else // if (!vReg.spillInfo.infiniteSpillCost)
high.set(i);
// Set coloring info.
vReg.spillInfo.willSpill = false;
switch(vReg.getClass())
{
case vrcInteger:
vReg.colorRegister(LAST_GREGISTER + 1);
break;
case vrcFloatingPoint:
case vrcFixedPoint:
vReg.colorRegister(LAST_FPREGISTER + 1);
break;
default:
PR_ASSERT(false); // Cannot happen.
}
}
}
// push the stack registers
PRInt32 j;
for (j = stack.firstOne(); j != -1; j = stack.nextOne(j))
*stackPtr++ = j;
// simplify
while (true)
{
PRInt32 r;
while ((r = getLowestSpillCostRegister(low)) != -1)
{
VirtualRegister& vReg = vRegManager.getVirtualRegister(r);
/* update low and high */
FastBitSet inter(interferenceMatrix.getRow(r), nRegisters);
for (j = inter.firstOne(); j != -1; j = inter.nextOne(j))
{
VirtualRegister& neighbor = vRegManager.getVirtualRegister(j);
// if the new interference degree of one of his neighbor becomes
// NUMBER_OF_REGISTERS - 1 then it is added to the set 'low'.
PRUint32 maxInterference = 0;
switch (neighbor.getClass())
{
case vrcInteger:
maxInterference = NUMBER_OF_GREGISTERS;
break;
case vrcFloatingPoint:
case vrcFixedPoint:
maxInterference = NUMBER_OF_FPREGISTERS;
break;
default:
PR_ASSERT(false);
}
if ((vRegManager.getVirtualRegister(j).colorInfo.interferenceDegree-- == maxInterference))
{
high.clear(j);
low.set(j);
}
vReg.colorInfo.interferenceDegree--;
interferenceMatrix.clear(r, j);
interferenceMatrix.clear(j, r);
}
low.clear(r);
// Push this register.
*stackPtr++ = r;
}
if ((r = getLowestSpillCostRegister(high)) != -1)
{
high.clear(r);
low.set(r);
}
else
break;
}
return stackPtr;
}
bool Coloring::
select(FastBitMatrix& interferenceMatrix, PRUint32* stackBase, PRUint32* stackPtr)
{
PRUint32 nRegisters = vRegManager.count();
FastBitSet usedRegisters(NUMBER_OF_REGISTERS + 1); // usedRegisters if used for both GR & FPR.
FastBitSet preColoredRegisters(NUMBER_OF_REGISTERS + 1);
FastBitSet usedStack(nRegisters + 1);
bool success = true;
Int32 lastUsedSSR = -1;
// select
while (stackPtr != stackBase)
{
// Pop one register.
PRUint32 r = *--stackPtr;
VirtualRegister& vReg = vRegManager.getVirtualRegister(r);
FastBitSet neighbors(interferenceMatrix.getRow(r), nRegisters);
if (vReg.getClass() == vrcStackSlot)
// Stack slots coloring.
{
usedStack.clear();
for (PRInt32 i = neighbors.firstOne(); i != -1; i = neighbors.nextOne(i))
usedStack.set(vRegManager.getVirtualRegister(i).getColor());
Int32 color = usedStack.firstZero();
vReg.colorRegister(color);
if (color > lastUsedSSR)
lastUsedSSR = color;
}
else
// Integer & Floating point register coloring.
{
usedRegisters.clear();
preColoredRegisters.clear();
for (PRInt32 i = neighbors.firstOne(); i != -1; i = neighbors.nextOne(i))
{
VirtualRegister& nvReg = vRegManager.getVirtualRegister(i);
usedRegisters.set(nvReg.getColor());
if (nvReg.isPreColored())
preColoredRegisters.set(nvReg.getPreColor());
}
if (vReg.hasSpecialInterference)
usedRegisters |= vReg.specialInterference;
PRInt8 c = -1;
PRInt8 maxColor = 0;
PRInt8 firstColor = 0;
switch (vReg.getClass())
{
case vrcInteger:
firstColor = FIRST_GREGISTER;
maxColor = LAST_GREGISTER;
break;
case vrcFloatingPoint:
case vrcFixedPoint:
firstColor = FIRST_FPREGISTER;
maxColor = LAST_FPREGISTER;
break;
default:
PR_ASSERT(false);
}
if (vReg.isPreColored())
{
c = vReg.getPreColor();
if (usedRegisters.test(c))
c = -1;
}
else
{
for (c = usedRegisters.nextZero(firstColor - 1); (c >= 0) && (c <= maxColor) && (preColoredRegisters.test(c));
c = usedRegisters.nextZero(c)) {}
}
if ((c >= 0) && (c <= maxColor))
{
vReg.colorRegister(c);
}
else
{
VirtualRegister& stackRegister = vRegManager.newVirtualRegister(vrcStackSlot);
vReg.equivalentRegister[vrcStackSlot] = &stackRegister;
vReg.spillInfo.willSpill = true;
success = false;
}
}
}
#ifdef DEBUG
if (success)
{
for (VirtualRegisterManager::iterator i = vRegManager.begin(); !vRegManager.done(i); i = vRegManager.advance(i))
{
VirtualRegister& vReg = vRegManager.getVirtualRegister(i);
switch (vReg.getClass())
{
case vrcInteger:
if (vReg.getColor() > LAST_GREGISTER)
PR_ASSERT(false);
break;
case vrcFloatingPoint:
case vrcFixedPoint:
#if NUMBER_OF_FPREGISTERS != 0
if (vReg.getColor() > LAST_FPREGISTER)
PR_ASSERT(false);
#endif
break;
default:
break;
}
}
}
#endif
vRegManager.nUsedStackSlots = lastUsedSSR + 1;
return success;
}
#endif // NEW_LAURENTM_CODE

View File

@@ -0,0 +1,284 @@
/* -*- 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "Fundamentals.h"
#include "ControlGraph.h"
#include "ControlNodes.h"
#include "Instruction.h"
#include "RegisterAllocator.h"
#include "VirtualRegister.h"
#include "InterferenceGraph.h"
#include "SparseSet.h"
#include "Spilling.h"
#include "Splits.h"
UT_EXTERN_LOG_MODULE(RegAlloc);
template <class RegisterPressure>
class Coloring
{
private:
static RegisterName* simplify(RegisterAllocator& registerAllocator, RegisterName* coloringStack);
static bool select(RegisterAllocator& registerAllocator, RegisterName* coloringStack, RegisterName* coloringStackPtr);
public:
static bool color(RegisterAllocator& registerAllocator);
static void finalColoring(RegisterAllocator& registerAllocator);
};
template <class RegisterPressure>
void Coloring<RegisterPressure>::finalColoring(RegisterAllocator& registerAllocator)
{
RegisterName* color = registerAllocator.color;
RegisterName* name2range = registerAllocator.name2range;
ControlGraph& controlGraph = registerAllocator.controlGraph;
ControlNode** nodes = controlGraph.dfsList;
Uint32 nNodes = controlGraph.nNodes;
for (Uint32 n = 0; n < nNodes; n++) {
InstructionList& instructions = nodes[n]->getInstructions();
for (InstructionList::iterator i = instructions.begin(); !instructions.done(i); i = instructions.advance(i)) {
Instruction& instruction = instructions.get(i);
InstructionUse* useEnd = instruction.getInstructionUseEnd();
for (InstructionUse* usePtr = instruction.getInstructionUseBegin(); usePtr < useEnd; usePtr++)
if (usePtr->isRegister()) {
usePtr->setRegisterName(color[name2range[usePtr->getRegisterName()]]);
#ifdef DEBUG
RegisterID rid = usePtr->getRegisterID();
setColoredRegister(rid);
usePtr->setRegisterID(rid);
#endif // DEBUG
}
InstructionDefine* defineEnd = instruction.getInstructionDefineEnd();
for (InstructionDefine* definePtr = instruction.getInstructionDefineBegin(); definePtr < defineEnd; definePtr++)
if (definePtr->isRegister()) {
definePtr->setRegisterName(color[name2range[definePtr->getRegisterName()]]);
#ifdef DEBUG
RegisterID rid = definePtr->getRegisterID();
setColoredRegister(rid);
definePtr->setRegisterID(rid);
#endif // DEBUG
}
}
}
}
template <class RegisterPressure>
bool Coloring<RegisterPressure>::select(RegisterAllocator& registerAllocator, RegisterName* coloringStack, RegisterName* coloringStackPtr)
{
Uint32 rangeCount = registerAllocator.rangeCount;
RegisterName* color = new RegisterName[rangeCount];
registerAllocator.color = color;
for (Uint32 r = 1; r < rangeCount; r++)
color[r] = RegisterName(6); // FIX;
// Color the preColored registers.
//
VirtualRegisterManager& vrManager = registerAllocator.vrManager;
RegisterName* name2range = registerAllocator.name2range;
PreColoredRegister* machineEnd = vrManager.getMachineRegistersEnd();
for (PreColoredRegister* machinePtr = vrManager.getMachineRegistersBegin(); machinePtr < machineEnd; machinePtr++)
if (machinePtr->id != invalidID) {
color[name2range[getName(machinePtr->id)]] = machinePtr->color;
UT_OBJECTLOG(UT_LOG_MODULE(RegAlloc), PR_LOG_ALWAYS, ("\twill preColor range %d as %d\n", name2range[getName(machinePtr->id)], machinePtr->color));
}
SpillCost* cost = registerAllocator.spillCost;
Pool& pool = registerAllocator.pool;
SparseSet& spill = *new(pool) SparseSet(pool, rangeCount);
registerAllocator.willSpill = &spill;
SparseSet neighborColors(pool, 6); // FIX
InterferenceGraph<RegisterPressure>& iGraph = registerAllocator.iGraph;
bool coloringFailed = false;
while (coloringStackPtr > coloringStack) {
RegisterName range = *--coloringStackPtr;
if (!cost[range].infinite && cost[range].cost < 0) {
coloringFailed = true;
spill.set(range);
UT_OBJECTLOG(UT_LOG_MODULE(RegAlloc), PR_LOG_ALWAYS, ("\tfailed to color %d, will spill.\n", range));
} else {
neighborColors.clear();
for (InterferenceVector* vector = iGraph.getInterferenceVector(range); vector != NULL; vector = vector->next)
for (Int32 i = vector->count - 1; i >= 0; --i) {
RegisterName neighborColor = color[vector->neighbors[i]];
if (neighborColor < 6) // FIX
neighborColors.set(neighborColor);
}
if (neighborColors.getSize() == 6) { // FIX
coloringFailed = true;
UT_OBJECTLOG(UT_LOG_MODULE(RegAlloc), PR_LOG_ALWAYS, ("\tfailed to color %d, ", range));
if (!Splits<RegisterPressure>::findSplit(registerAllocator, color, range)) {
UT_OBJECTLOG(UT_LOG_MODULE(RegAlloc), PR_LOG_ALWAYS, ("will spill.\n"));
spill.set(range);
} else
UT_OBJECTLOG(UT_LOG_MODULE(RegAlloc), PR_LOG_ALWAYS, ("will split.\n"));
} else {
for (Uint32 i = 0; i < 6; i++) // FIX
if (!neighborColors.test(i)) {
fprintf(stdout, "\twill color %d as %d\n", range, i);
color[range] = RegisterName(i);
break;
}
}
}
}
#ifdef DEBUG_LOG
if (coloringFailed) {
UT_OBJECTLOG(UT_LOG_MODULE(RegAlloc), PR_LOG_ALWAYS, ("Coloring failed:\n"));
UT_OBJECTLOG(UT_LOG_MODULE(RegAlloc), PR_LOG_ALWAYS, ("\twill spill: "));
spill.printPretty(UT_LOG_MODULE(RegAlloc));
} else {
UT_OBJECTLOG(UT_LOG_MODULE(RegAlloc), PR_LOG_ALWAYS, ("Coloring succeeded:\n"));
for (Uint32 i = 1; i < rangeCount; i++)
UT_OBJECTLOG(UT_LOG_MODULE(RegAlloc), PR_LOG_ALWAYS, ("\trange %d colored as %d\n", i, color[i]));
}
#endif
return !coloringFailed;
}
template <class RegisterPressure>
RegisterName* Coloring<RegisterPressure>::simplify(RegisterAllocator& registerAllocator, RegisterName* coloringStack)
{
InterferenceGraph<RegisterPressure>& iGraph = registerAllocator.iGraph;
SpillCost* spillCost = registerAllocator.spillCost;
Uint32 rangeCount = registerAllocator.rangeCount;
Uint32* degree = new Uint32[rangeCount];
for (RegisterName i = RegisterName(1); i < rangeCount; i = RegisterName(i + 1)) {
InterferenceVector* vector = iGraph.getInterferenceVector(i);
degree[i] = (vector != NULL) ? vector->count : 0;
}
Pool& pool = registerAllocator.pool;
SparseSet low(pool, rangeCount);
SparseSet high(pool, rangeCount);
SparseSet highInfinite(pool, rangeCount);
SparseSet preColored(pool, rangeCount);
// Get the precolored registers.
//
VirtualRegisterManager& vrManager = registerAllocator.vrManager;
RegisterName* name2range = registerAllocator.name2range;
PreColoredRegister* machineEnd = vrManager.getMachineRegistersEnd();
for (PreColoredRegister* machinePtr = vrManager.getMachineRegistersBegin(); machinePtr < machineEnd; machinePtr++)
if (machinePtr->id != invalidID)
preColored.set(name2range[getName(machinePtr->id)]);
// Insert the live ranges in the sets.
//
for (Uint32 range = 1; range < rangeCount; range++)
if (!preColored.test(range))
if (degree[range] < 6) // FIX
low.set(range);
else if (!spillCost[range].infinite)
high.set(range);
else
highInfinite.set(range);
#ifdef DEBUG_LOG
UT_OBJECTLOG(UT_LOG_MODULE(RegAlloc), PR_LOG_ALWAYS, ("Coloring sets:\n\tlow = "));
low.printPretty(UT_LOG_MODULE(RegAlloc));
UT_OBJECTLOG(UT_LOG_MODULE(RegAlloc), PR_LOG_ALWAYS, ("\thigh = "));
high.printPretty(UT_LOG_MODULE(RegAlloc));
UT_OBJECTLOG(UT_LOG_MODULE(RegAlloc), PR_LOG_ALWAYS, ("\thighInfinite = "));
highInfinite.printPretty(UT_LOG_MODULE(RegAlloc));
UT_OBJECTLOG(UT_LOG_MODULE(RegAlloc), PR_LOG_ALWAYS, ("\tpreColored = "));
preColored.printPretty(UT_LOG_MODULE(RegAlloc));
#endif // DEBUG_LOG
RegisterName* coloringStackPtr = coloringStack;
while (low.getSize() != 0 || high.getSize() != 0) {
while (low.getSize() != 0) {
RegisterName range = RegisterName(low.getOne());
low.clear(range);
*coloringStackPtr++ = range;
for (InterferenceVector* vector = iGraph.getInterferenceVector(range); vector != NULL; vector = vector->next)
for (Int32 i = (vector->count - 1); i >= 0; --i) {
RegisterName neighbor = vector->neighbors[i];
degree[neighbor]--;
if (degree[neighbor] < 6) // FIX
if (high.test(neighbor)) {
high.clear(neighbor);
low.set(neighbor);
} else if (highInfinite.test(neighbor)) {
highInfinite.clear(neighbor);
low.set(neighbor);
}
}
}
if (high.getSize() != 0) {
RegisterName best = RegisterName(high.getOne());
double bestCost = spillCost[best].cost;
double bestDegree = degree[best];
// Choose the next best candidate.
//
for (SparseSet::iterator i = high.begin(); !high.done(i); i = high.advance(i)) {
RegisterName range = RegisterName(high.get(i));
double thisCost = spillCost[range].cost;
double thisDegree = degree[range];
if (thisCost * bestDegree < bestCost * thisDegree) {
best = range;
bestCost = thisCost;
bestDegree = thisDegree;
}
}
high.clear(best);
low.set(best);
}
}
assert(highInfinite.getSize() == 0);
delete degree;
#ifdef DEBUG_LOG
UT_OBJECTLOG(UT_LOG_MODULE(RegAlloc), PR_LOG_ALWAYS, ("Coloring stack:\n\t"));
for (RegisterName* sp = coloringStack; sp < coloringStackPtr; ++sp)
UT_OBJECTLOG(UT_LOG_MODULE(RegAlloc), PR_LOG_ALWAYS, ("%d ", *sp));
UT_OBJECTLOG(UT_LOG_MODULE(RegAlloc), PR_LOG_ALWAYS, ("\n"));
#endif // DEBUG_LOG
return coloringStackPtr;
}
template <class RegisterPressure>
bool Coloring<RegisterPressure>::color(RegisterAllocator& registerAllocator)
{
RegisterName* coloringStack = new RegisterName[registerAllocator.rangeCount];
return select(registerAllocator, coloringStack, simplify(registerAllocator, coloringStack));
}

View File

@@ -0,0 +1,212 @@
/* -*- 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "Fundamentals.h"
#include <string.h>
#include "ControlGraph.h"
#include "ControlNodes.h"
#include "DominatorGraph.h"
DominatorGraph::DominatorGraph(ControlGraph& controlGraph) : controlGraph(controlGraph)
{
Uint32 nNodes = controlGraph.nNodes;
GtoV = new Uint32[nNodes + 1];
VtoG = new Uint32[nNodes + 1];
Uint32 v = 1;
for (Uint32 n = 0; n < nNodes; n++) {
VtoG[v] = n;
GtoV[n] = v++;
}
// Initialize all the 1-based arrays.
//
parent = new Uint32[v];
semi = new Uint32[v];
vertex = new Uint32[v];
label = new Uint32[v];
size = new Uint32[v];
ancestor = new Uint32[v];
child = new Uint32[v];
dom = new Uint32[v];
bucket = new DGLinkedList*[v];
memset(semi, '\0', v * sizeof(Uint32));
memset(bucket, '\0', v * sizeof(DGLinkedList*));
vCount = v;
build();
delete parent;
delete semi;
delete vertex;
delete label;
delete size;
delete ancestor;
delete child;
delete dom;
delete bucket;
}
Uint32 DominatorGraph::DFS(Uint32 vx, Uint32 n)
{
semi[vx] = ++n;
vertex[n] = label[vx] = vx;
ancestor[vx] = child[vx] = 0;
size[vx] = 1;
ControlNode& node = *controlGraph.dfsList[VtoG[vx]];
ControlEdge* successorEnd = node.getSuccessorsEnd();
for (ControlEdge* successorPtr = node.getSuccessorsBegin(); successorPtr < successorEnd; successorPtr++) {
Uint32 w = GtoV[successorPtr->getTarget().dfsNum];
if (semi[w] == 0) {
parent[w] = vx;
n = DFS(w, n);
}
}
return n;
}
void DominatorGraph::LINK(Uint32 vx, Uint32 w)
{
Uint32 s = w;
while (semi[label[w]] < semi[label[child[s]]]) {
if (size[s] + size[child[child[s]]] >= (size[child[s]] << 1)) {
ancestor[child[s]] = s;
child[s] = child[child[s]];
} else {
size[child[s]] = size[s];
s = ancestor[s] = child[s];
}
}
label[s] = label[w];
size[vx] += size[w];
if(size[vx] < (size[w] << 1)) {
Uint32 t = s;
s = child[vx];
child[vx] = t;
}
while( s != 0 ) {
ancestor[s] = vx;
s = child[s];
}
}
void DominatorGraph::COMPRESS(Uint32 vx)
{
if(ancestor[ancestor[vx]] != 0) {
COMPRESS(ancestor[vx]);
if(semi[label[ancestor[vx]]] < semi[label[vx]])
label[vx] = label[ancestor[vx]];
ancestor[vx] = ancestor[ancestor[vx]];
}
}
Uint32 DominatorGraph::EVAL(Uint32 vx)
{
if(ancestor[vx] == 0)
return label[vx];
COMPRESS(vx);
return (semi[label[ancestor[vx]]] >= semi[label[vx]]) ? label[vx] : label[ancestor[vx]];
}
void DominatorGraph::build()
{
Uint32 n = DFS(GtoV[0], 0);
size[0] = label[0] = semi[0];
for (Uint32 i = n; i >= 2; i--) {
Uint32 w = vertex[i];
ControlNode& node = *controlGraph.dfsList[VtoG[w]];
const DoublyLinkedList<ControlEdge>& predecessors = node.getPredecessors();
for (DoublyLinkedList<ControlEdge>::iterator p = predecessors.begin(); !predecessors.done(p); p = predecessors.advance(p)) {
Uint32 vx = GtoV[predecessors.get(p).getSource().dfsNum];
Uint32 u = EVAL(vx);
if(semi[u] < semi[w])
semi[w] = semi[u];
}
DGLinkedList* elem = new DGLinkedList();
elem->next = bucket[vertex[semi[w]]];
elem->index = w;
bucket[vertex[semi[w]]] = elem;
LINK(parent[w], w);
elem = bucket[parent[w]];
while(elem != NULL) {
Uint32 vx = elem->index;
Uint32 u = EVAL(vx);
dom[vx] = (semi[u] < semi[vx]) ? u : parent[w];
elem = elem->next;
}
}
memset(size, '\0', n * sizeof(Uint32));
Pool& pool = controlGraph.pool;
nodes = new(pool) DGNode[n];
for(Uint32 j = 2; j <= n; j++) {
Uint32 w = vertex[j];
Uint32 d = dom[w];
if(d != vertex[semi[w]]) {
d = dom[d];
dom[w] = d;
}
size[d]++;
}
dom[GtoV[0]] = 0;
for (Uint32 k = 1; k <= n; k++) {
DGNode& node = nodes[VtoG[k]];
Uint32 count = size[k];
node.successorsEnd = node.successorsBegin = (count) ? new(pool) Uint32[count] : (Uint32*) 0;
}
for (Uint32 l = 2; l <= n; l++)
*(nodes[VtoG[dom[l]]].successorsEnd)++ = VtoG[l];
}
#ifdef DEBUG_LOG
void DominatorGraph::printPretty(LogModuleObject log)
{
UT_OBJECTLOG(log, PR_LOG_ALWAYS, ("Dominator Graph:\n"));
Uint32 nNodes = controlGraph.nNodes;
for (Uint32 i = 0; i < nNodes; i++) {
DGNode& node = nodes[i];
if (node.successorsBegin != node.successorsEnd) {
UT_OBJECTLOG(log, PR_LOG_ALWAYS, ("\tN%d dominates ", i));
for (Uint32* successorsPtr = node.successorsBegin; successorsPtr < node.successorsEnd; successorsPtr++)
UT_OBJECTLOG(log, PR_LOG_ALWAYS, ("N%d ", *successorsPtr));
UT_OBJECTLOG(log, PR_LOG_ALWAYS, ("\n"));
}
}
}
#endif // DEBUG_LOG

View File

@@ -0,0 +1,80 @@
/* -*- 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#ifndef _DOMINATOR_GRAPH_H_
#define _DOMINATOR_GRAPH_H_
#include "LogModule.h"
class ControlGraph;
struct DGNode
{
Uint32* successorsBegin;
Uint32* successorsEnd;
};
struct DGLinkedList
{
DGLinkedList* next;
Uint32 index;
};
class DominatorGraph
{
private:
ControlGraph& controlGraph;
Uint32 vCount;
Uint32* VtoG;
Uint32* GtoV;
Uint32* parent;
Uint32* semi;
Uint32* vertex;
Uint32* label;
Uint32* size;
Uint32* ancestor;
Uint32* child;
Uint32* dom;
DGLinkedList** bucket;
DGNode* nodes;
private:
void build();
Uint32 DFS(Uint32 vx, Uint32 n);
void LINK(Uint32 vx, Uint32 w);
void COMPRESS(Uint32 vx);
Uint32 EVAL(Uint32 vx);
public:
DominatorGraph(ControlGraph& controlGraph);
Uint32* getSuccessorsBegin(Uint32 n) const {return nodes[n].successorsBegin;}
Uint32* getSuccessorsEnd(Uint32 n) const {return nodes[n].successorsEnd;}
#ifdef DEBUG_LOG
void printPretty(LogModuleObject log);
#endif // DEBUG_LOG
};
#endif // _DOMINATOR_GRAPH_H_

View File

@@ -1,5 +1,4 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
/* -*- 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.0 (the "NPL"); you may not use this file except in
@@ -13,20 +12,9 @@
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
// API class
package org.mozilla.javascript;
/**
* Thrown if errors are detected while attempting to define a host object
* from a Java class.
*/
public class ClassDefinitionException extends Exception {
public ClassDefinitionException(String detail) {
super(detail);
}
}
#include "Fundamentals.h"
#include "HashSet.h"

View File

@@ -0,0 +1,97 @@
/* -*- 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#ifndef _HASH_SET_H_
#define _HASH_SET_H_
#include "Fundamentals.h"
#include "Pool.h"
#include <string.h>
struct HashSetElement
{
Uint32 index;
HashSetElement* next;
};
class HashSet
{
private:
static const hashSize = 64;
// Return the hash code for the given element index.
static Uint32 getHashCode(Uint32 index) {return index & (hashSize - 1);} // Could be better !
private:
Pool& allocationPool;
HashSetElement** bucket;
HashSetElement* free;
private:
// No copy constructor.
HashSet(const HashSet&);
// No copy operator.
void operator = (const HashSet&);
public:
// Create a new HashSet.
inline HashSet(Pool& pool, Uint32 universeSize);
// Clear the hashset.
void clear();
// Clear the element for the given index.
void clear(Uint32 index);
// Set the element for the given index.
void set(Uint32 index);
// Return true if the element at index is a member.
bool test(Uint32 index) const;
// Union with the given hashset.
inline void or(const HashSet& set);
// Intersection with the given hashset.
inline void and(const HashSet& set);
// Difference with the given hashset.
inline void difference(const HashSet& set);
// Logical operators.
HashSet& operator |= (const HashSet& set) {or(set); return *this;}
HashSet& operator &= (const HashSet& set) {and(set); return *this;}
HashSet& operator -= (const HashSet& set) {difference(set); return *this;}
// Iterator to conform with the set API.
typedef HashSetElement* iterator;
// Return the iterator for the first element of this set.
iterator begin() const;
// Return the next iterator.
iterator advance(iterator pos) const;
// Return true if the iterator is at the end of the set.
bool done(iterator pos) const {return pos == NULL;}
};
inline HashSet::HashSet(Pool& pool, Uint32 /*universeSize*/)
: allocationPool(pool), free(NULL)
{
bucket = new(pool) HashSetElement*[hashSize];
memset(bucket, '\0', sizeof(HashSetElement*));
}
#endif // _HASH_SET_H_

View File

@@ -0,0 +1,213 @@
/* -*- 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#ifndef _INDEXED_POOL_H_
#define _INDEXED_POOL_H_
#include "Fundamentals.h"
#include <string.h>
#include <stdlib.h>
//------------------------------------------------------------------------------
// IndexedPool<IndexedObjectSubclass> is an indexed pool of objects. The
// template parameter 'IndexedObjectSubclass' must be a subclass of the struct
// IndexedObject.
//
// When the indexed pool is ask to allocate and initialize a new object (using
// the operator new(anIndexedPool) it will zero the memory used to store the
// object and initialize the field 'index' of this object to its position in
// the pool.
//
// An object allocated by the indexed pool can be freed by calling the method
// IndexedPool::release(IndexedElement& objectIndex).
//
// example:
//
// IndexedPool<IndexedElement> elementPool;
//
// IndexedElement& element1 = *new(elementPool) IndexedElement();
// IndexedElement& element2 = *new(elementPool) IndexedElement();
//
// indexedPool.release(element1);
// IndexedElement& element3 = *new(elementPool) IndexedElement();
//
// At this point element1 is no longer a valid object, element2 is at
// index 2 and element3 is at index 1.
//
//------------------------------------------------------------------------------
// IndexedObject -
//
template<class Object>
struct IndexedObject
{
Uint32 index; // Index in the pool.
Object* next; // Used to link IndexedObject together.
Uint32 getIndex() {return index;}
};
//------------------------------------------------------------------------------
// IndexedPool<IndexedObject> -
//
template <class IndexedObject>
class IndexedPool
{
private:
static const blockSize = 4; // Size of one block.
Uint32 nBlocks; // Number of blocks in the pool.
IndexedObject** block; // Array of block pointers.
IndexedObject* freeObjects; // Chained list of free IndexedObjects.
Uint32 nextIndex; // Index of the next free object in the last block.
private:
void allocateAnotherBlock();
IndexedObject& newObject();
public:
IndexedPool() : nBlocks(0), block(NULL), freeObjects(NULL), nextIndex(1) {}
~IndexedPool();
IndexedObject& get(Uint32 index) const;
void release(IndexedObject& object);
void setSize(Uint32 size) {assert(size < nextIndex); nextIndex = size;}
// Return the universe size.
Uint32 getSize() {return nextIndex;}
friend void* operator new(size_t, IndexedPool<IndexedObject>& pool); // Needs to call newObject().
};
// Free all the memory allocated for this object.
//
template <class IndexedObject>
IndexedPool<IndexedObject>::~IndexedPool()
{
for (Uint32 n = 0; n < nBlocks; n++)
free(&((IndexedObject **) &block[n][n*blockSize])[-(n + 1)]);
}
// Release the given. This object will be iserted in the chained
// list of free IndexedObjects. To minimize the fragmentation the chained list
// is ordered by ascending indexes.
//
template <class IndexedObject>
void IndexedPool<IndexedObject>::release(IndexedObject& object)
{
Uint32 index = object.index;
IndexedObject* list = freeObjects;
assert(&object == &get(index)); // Make sure that object is owned by this pool.
if (list == NULL) { // The list is empty.
freeObjects = &object;
object.next = NULL;
} else { // The list contains at least 1 element.
if (index < list->index) { // insert as first element.
freeObjects = &object;
object.next = list;
} else { // Find this object's place.
while ((list->next) != NULL && (list->next->index < index))
list = list->next;
object.next = list->next;
list->next = &object;
}
}
#ifdef DEBUG
// Sanity check to be sure that the list is correctly ordered.
for (IndexedObject* obj = freeObjects; obj != NULL; obj = obj->next)
if (obj->next != NULL)
assert(obj->index < obj->next->index);
#endif
}
// Create a new block of IndexedObjects. We will allocate the memory to
// store IndexedPool::blockSize IndexedObject and the new Array of block
// pointers.
// The newly created IndexedObjects will not be initialized.
//
template <class IndexedObject>
void IndexedPool<IndexedObject>::allocateAnotherBlock()
{
void* memory = (void *) malloc((nBlocks + 1) * sizeof(Uint32) + blockSize * sizeof(IndexedObject));
memcpy(memory, block, nBlocks * sizeof(Uint32));
block = (IndexedObject **) memory;
IndexedObject* objects = (IndexedObject *) &block[nBlocks + 1];
block[nBlocks] = &objects[-(nBlocks * blockSize)];
nBlocks++;
}
// Return the IndexedObject at the position 'index' in the pool.
//
template <class IndexedObject>
IndexedObject& IndexedPool<IndexedObject>::get(Uint32 index) const
{
Uint32 blockIndex = index / blockSize;
assert(blockIndex < nBlocks);
return block[blockIndex][index];
}
// Return the reference of an unused object in the pool.
//
template <class IndexedObject>
IndexedObject& IndexedPool<IndexedObject>::newObject()
{
if (freeObjects != NULL) {
IndexedObject& newObject = *freeObjects;
freeObjects = newObject.next;
return newObject;
}
Uint32 nextIndex = this->nextIndex++;
Uint32 blockIndex = nextIndex / blockSize;
while (blockIndex >= nBlocks)
allocateAnotherBlock();
IndexedObject& newObject = block[blockIndex][nextIndex];
newObject.index = nextIndex;
return newObject;
}
// Return the address of the next unsused object in the given
// indexed pool. The field index of the newly allocated object
// will be initialized to the corresponding index of this object
// in the pool.
//
template <class IndexedObject>
void* operator new(size_t size, IndexedPool<IndexedObject>& pool)
{
assert(size == sizeof(IndexedObject));
return (void *) &pool.newObject();
}
#endif // _INDEXED_POOL_H_

View File

@@ -0,0 +1,258 @@
/* -*- 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#ifndef _INTERFERENCE_GRAPH_H_
#define _INTERFERENCE_GRAPH_H_
#include "Fundamentals.h"
#include "ControlGraph.h"
#include "Primitives.h"
#include "Instruction.h"
#include "VirtualRegister.h"
#include "RegisterPressure.h"
#include "SparseSet.h"
#include <string.h>
struct InterferenceVector
{
Uint32 count;
InterferenceVector* next;
RegisterName* neighbors;
InterferenceVector() : count(0), next(NULL) {}
};
class RegisterAllocator;
template <class RegisterPressure>
class InterferenceGraph
{
private:
RegisterAllocator& registerAllocator;
RegisterPressure::Set* interferences;
InterferenceVector** vector;
Uint32* offset;
Uint32 rangeCount;
private:
// No copy constructor.
InterferenceGraph(const InterferenceGraph&);
// No copy operator.
void operator = (const InterferenceGraph&);
// Check if reg is a member of the universe.
void checkMember(RegisterName name) {assert(name < rangeCount);}
// Return the edge index for the interference between name1 and name2.
Uint32 getEdgeIndex(RegisterName name1, RegisterName name2);
public:
InterferenceGraph(RegisterAllocator& registerAllocator) : registerAllocator(registerAllocator) {}
// Calculate the interferences.
void build();
// Return true if reg1 and reg2 interfere.
bool interfere(RegisterName name1, RegisterName name2);
// Return the interference vector for the given register or NULL if there is none.
InterferenceVector* getInterferenceVector(RegisterName name) {return vector[name];}
// Set the interference between name1 and name2.
void setInterference(RegisterName name1, RegisterName name2);
// Set the interference vector for the given register.
void setInterferenceVector(RegisterName name, InterferenceVector* v) {vector[name] = v;}
#ifdef DEBUG_LOG
// Print the interferences.
void printPretty(LogModuleObject log);
#endif // DEBUG_LOG
};
template <class RegisterPressure>
void InterferenceGraph<RegisterPressure>::build()
{
Pool& pool = registerAllocator.pool;
Uint32 rangeCount = registerAllocator.rangeCount;
this->rangeCount = rangeCount;
// Initialize the structures.
//
offset = new(pool) Uint32[rangeCount + 1];
vector = new(pool) InterferenceVector*[rangeCount];
memset(vector, '\0', sizeof(InterferenceVector*) * rangeCount);
Uint32 o = 0;
offset[0] = 0;
for (Uint32 i = 1; i <= rangeCount; ++i) {
offset[i] = o;
o += i;
}
interferences = new(pool) RegisterPressure::Set(pool, (rangeCount * rangeCount) / 2);
ControlGraph& controlGraph = registerAllocator.controlGraph;
ControlNode** nodes = controlGraph.dfsList;
Uint32 nNodes = controlGraph.nNodes;
RegisterName* name2range = registerAllocator.name2range;
LivenessInfo<RegisterPressure> liveness = Liveness<RegisterPressure>::analysis(controlGraph, rangeCount, name2range);
registerAllocator.liveness = liveness;
SparseSet currentLive(pool, rangeCount);
for (Uint32 n = 0; n < nNodes; n++) {
ControlNode& node = *nodes[n];
currentLive = liveness.liveOut[n];
InstructionList& instructions = node.getInstructions();
for (InstructionList::iterator i = instructions.end(); !instructions.done(i); i = instructions.retreat(i)) {
Instruction& instruction = instructions.get(i);
InstructionUse* useBegin = instruction.getInstructionUseBegin();
InstructionUse* useEnd = instruction.getInstructionUseEnd();
InstructionUse* usePtr;
InstructionDefine* defineBegin = instruction.getInstructionDefineBegin();
InstructionDefine* defineEnd = instruction.getInstructionDefineEnd();
InstructionDefine* definePtr;
// Handle the copy instruction to avoid unnecessary interference between the 2 registers.
if ((instruction.getFlags() & ifCopy) != 0) {
assert(useBegin != useEnd && useBegin[0].isRegister());
currentLive.clear(name2range[useBegin[0].getRegisterName()]);
}
// Create the interferences.
for (definePtr = defineBegin; definePtr < defineEnd; definePtr++)
if (definePtr->isRegister()) {
RegisterName define = name2range[definePtr->getRegisterName()];
for (SparseSet::iterator e = currentLive.begin(); !currentLive.done(e); e = currentLive.advance(e)) {
RegisterName live = RegisterName(currentLive.get(e));
if ((live != define) && !interfere(live, define) && registerAllocator.canInterfere(live, define)) {
if (vector[define] == NULL)
vector[define] = new(pool) InterferenceVector();
vector[define]->count++;
if (vector[live] == NULL)
vector[live] = new(pool) InterferenceVector();
vector[live]->count++;
setInterference(live, define);
}
}
}
// Now update the liveness.
//
for (definePtr = defineBegin; definePtr < defineEnd; definePtr++)
if (definePtr->isRegister())
currentLive.clear(name2range[definePtr->getRegisterName()]);
for (usePtr = useBegin; usePtr < useEnd; usePtr++)
if (usePtr->isRegister())
currentLive.set(name2range[usePtr->getRegisterName()]);
}
}
// Allocate the memory to store the interferences.
//
for (Uint32 e = 0; e < rangeCount; e++)
if (vector[e] != NULL) {
InterferenceVector& v = *vector[e];
v.neighbors = new(pool) RegisterName[v.count];
v.count = 0;
}
// Initialize the edges.
//
if (RegisterPressure::Set::isOrdered()) {
RegisterName name1 = RegisterName(0);
for (RegisterPressure::Set::iterator i = interferences->begin(); !interferences->done(i); i = interferences->advance(i)) {
Uint32 interferenceIndex = interferences->get(i);
while(interferenceIndex >= offset[name1 + 1])
name1 = RegisterName(name1 + 1);
assert((interferenceIndex >= offset[name1]) && (interferenceIndex < offset[name1 + 1]));
RegisterName name2 = RegisterName(interferenceIndex - offset[name1]);
assert(interfere(name1, name2));
InterferenceVector& vector1 = *vector[name1];
vector1.neighbors[vector1.count++] = name2;
InterferenceVector& vector2 = *vector[name2];
vector2.neighbors[vector2.count++] = name1;
}
} else {
trespass("not Implemented"); // FIX: need one more pass to initialize the vectors.
}
}
template <class RegisterPressure>
Uint32 InterferenceGraph<RegisterPressure>::getEdgeIndex(RegisterName name1, RegisterName name2)
{
checkMember(name1); checkMember(name2);
assert(name1 != name2); // This is not possible.
return (name1 < name2) ? offset[name2] + name1 : offset[name1] + name2;
}
template <class RegisterPressure>
void InterferenceGraph<RegisterPressure>::setInterference(RegisterName name1, RegisterName name2)
{
interferences->set(getEdgeIndex(name1, name2));
}
template <class RegisterPressure>
bool InterferenceGraph<RegisterPressure>::interfere(RegisterName name1, RegisterName name2)
{
return interferences->test(getEdgeIndex(name1, name2));
}
#ifdef DEBUG_LOG
template <class RegisterPressure>
void InterferenceGraph<RegisterPressure>::printPretty(LogModuleObject log)
{
UT_OBJECTLOG(log, PR_LOG_ALWAYS, ("Interference Vectors:\n"));
for (Uint32 i = 1; i < rangeCount; i++) {
if (vector[i] != NULL) {
UT_OBJECTLOG(log, PR_LOG_ALWAYS, ("\tvr%d: (", i));
for (InterferenceVector* v = vector[i]; v != NULL; v = v->next)
for (Uint32 j = 0; j < v->count; j++) {
UT_OBJECTLOG(log, PR_LOG_ALWAYS, ("%d", v->neighbors[j]));
if (v->next != NULL || j != (v->count - 1))
UT_OBJECTLOG(log, PR_LOG_ALWAYS, (","));
}
UT_OBJECTLOG(log, PR_LOG_ALWAYS, (")\n"));
}
}
UT_OBJECTLOG(log, PR_LOG_ALWAYS, ("Interference Matrix:\n"));
for (RegisterName name1 = RegisterName(1); name1 < rangeCount; name1 = RegisterName(name1 + 1)) {
UT_OBJECTLOG(log, PR_LOG_ALWAYS, ("\t%d:\t", name1));
for (RegisterName name2 = RegisterName(1); name2 < rangeCount; name2 = RegisterName(name2 + 1))
UT_OBJECTLOG(log, PR_LOG_ALWAYS, ("%c", ((name1 != name2) && interfere(name1, name2)) ? '1' : '0'));
UT_OBJECTLOG(log, PR_LOG_ALWAYS, ("\n"));
}
}
#endif // DEBUG_LOG
#endif // _INTERFERENCE_GRAPH_H_

View File

@@ -0,0 +1,87 @@
/* -*- 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#ifndef _LIVE_RANGE_H_
#define _LIVE_RANGE_H_
#include "Fundamentals.h"
#include "ControlGraph.h"
#include "ControlNodes.h"
#include "Primitives.h"
#include "Instruction.h"
#include "RegisterAllocator.h"
#include "RegisterAllocatorTools.h"
template <class RegisterPressure>
struct LiveRange
{
static void build(RegisterAllocator& registerAllocator);
};
template <class RegisterPressure>
void LiveRange<RegisterPressure>::build(RegisterAllocator& registerAllocator)
{
// Intialize the lookup table.
//
Uint32 nameCount = registerAllocator.nameCount;
RegisterName* nameTable = new(registerAllocator.pool) RegisterName[2*nameCount];
RegisterName* rangeName = &nameTable[nameCount];
init(rangeName, nameCount);
// Walk the graph.
//
ControlGraph& controlGraph = registerAllocator.controlGraph;
ControlNode** nodes = controlGraph.dfsList;
Uint32 nNodes = controlGraph.nNodes;
SparseSet destination(registerAllocator.pool, nameCount);
for (Uint32 n = 0; n < nNodes; n++) {
InstructionList& phiNodes = nodes[n]->getPhiNodeInstructions();
destination.clear();
for (InstructionList::iterator i = phiNodes.begin(); !phiNodes.done(i); i = phiNodes.advance(i)) {
Instruction& phiNode = phiNodes.get(i);
assert(phiNode.getInstructionDefineBegin() != phiNode.getInstructionDefineEnd() && phiNode.getInstructionDefineBegin()[0].isRegister());
destination.set(findRoot(phiNode.getInstructionDefineBegin()[0].getRegisterName(), rangeName));
}
for (InstructionList::iterator p = phiNodes.begin(); !phiNodes.done(p); p = phiNodes.advance(p)) {
Instruction& phiNode = phiNodes.get(p);
assert(phiNode.getInstructionDefineBegin() != phiNode.getInstructionDefineEnd() && phiNode.getInstructionDefineBegin()[0].isRegister());
RegisterName destinationName = phiNode.getInstructionDefineBegin()[0].getRegisterName();
RegisterName destinationRoot = findRoot(destinationName, rangeName);
InstructionUse* useEnd = phiNode.getInstructionUseEnd();
for (InstructionUse* usePtr = phiNode.getInstructionUseBegin(); usePtr < useEnd; usePtr++) {
assert(usePtr->isRegister());
RegisterName sourceName = usePtr->getRegisterName();
RegisterName sourceRoot = findRoot(sourceName, rangeName);
if (sourceRoot != destinationRoot && !destination.test(sourceRoot))
rangeName[sourceRoot] = destinationRoot;
}
}
}
registerAllocator.rangeCount = compress(registerAllocator.name2range, rangeName, nameCount, nameCount);
}
#endif // _LIVE_RANGE_H_

View File

@@ -0,0 +1,163 @@
/* -*- 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#ifndef _LIVE_RANGE_GRAPH_
#define _LIVE_RANGE_GRAPH_
#include "Fundamentals.h"
#include "Pool.h"
#include "ControlGraph.h"
#include "ControlNodes.h"
#include "Instruction.h"
#include "RegisterTypes.h"
class RegisterAllocator;
template <class RegisterPressure>
class LiveRangeGraph
{
private:
RegisterAllocator& registerAllocator;
RegisterPressure::Set* edges;
Uint32 rangeCount;
public:
//
//
LiveRangeGraph(RegisterAllocator& registerAllocator) : registerAllocator(registerAllocator) {}
//
//
void build();
//
//
void addEdge(RegisterName name1, RegisterName name2);
//
//
bool haveEdge(RegisterName name1, RegisterName name2);
#ifdef DEBUG_LOG
//
//
void printPretty(LogModuleObject log);
#endif // DEBUG_LOG
};
template <class RegisterPressure>
void LiveRangeGraph<RegisterPressure>::build()
{
Pool& pool = registerAllocator.pool;
Uint32 rangeCount = registerAllocator.rangeCount;
this->rangeCount = rangeCount;
edges = new(pool) RegisterPressure::Set(pool, rangeCount * rangeCount);
ControlGraph& controlGraph = registerAllocator.controlGraph;
ControlNode** nodes = controlGraph.dfsList;
Uint32 nNodes = controlGraph.nNodes;
RegisterName* name2range = registerAllocator.name2range;
LivenessInfo<RegisterPressure>& liveness = registerAllocator.liveness;
SparseSet currentLive(pool, rangeCount);
for (Uint32 n = 0; n < nNodes; n++) {
ControlNode& node = *nodes[n];
currentLive = liveness.liveOut[n];
InstructionList& instructions = node.getInstructions();
for (InstructionList::iterator i = instructions.end(); !instructions.done(i); i = instructions.retreat(i)) {
Instruction& instruction = instructions.get(i);
InstructionUse* useBegin = instruction.getInstructionUseBegin();
InstructionUse* useEnd = instruction.getInstructionUseEnd();
InstructionUse* usePtr;
InstructionDefine* defineBegin = instruction.getInstructionDefineBegin();
InstructionDefine* defineEnd = instruction.getInstructionDefineEnd();
InstructionDefine* definePtr;
if ((instruction.getFlags() & ifCopy) != 0) {
assert(useBegin != useEnd && useBegin[0].isRegister());
currentLive.clear(name2range[useBegin[0].getRegisterName()]);
}
for (definePtr = defineBegin; definePtr < defineEnd; definePtr++)
if (definePtr->isRegister()) {
RegisterName define = name2range[definePtr->getRegisterName()];
for (SparseSet::iterator l = currentLive.begin(); !currentLive.done(l); l = currentLive.advance(l)) {
RegisterName live = RegisterName(currentLive.get(l));
if (define != live && registerAllocator.canInterfere(define, live))
addEdge(define, live);
}
}
for (definePtr = defineBegin; definePtr < defineEnd; definePtr++)
if (definePtr->isRegister())
currentLive.clear(name2range[definePtr->getRegisterName()]);
for (usePtr = useBegin; usePtr < useEnd; usePtr++)
if (usePtr->isRegister())
currentLive.set(name2range[usePtr->getRegisterName()]);
for (usePtr = useBegin; usePtr < useEnd; usePtr++)
if (usePtr->isRegister()) {
RegisterName use = name2range[usePtr->getRegisterName()];
for (SparseSet::iterator l = currentLive.begin(); !currentLive.done(l); l = currentLive.advance(l)) {
RegisterName live = RegisterName(currentLive.get(l));
if (use != live && registerAllocator.canInterfere(use, live))
addEdge(use, live);
}
}
}
}
}
template <class RegisterPressure>
void LiveRangeGraph<RegisterPressure>::addEdge(RegisterName name1, RegisterName name2)
{
assert(name1 != name2);
edges->set(name1 * rangeCount + name2);
}
template <class RegisterPressure>
bool LiveRangeGraph<RegisterPressure>::haveEdge(RegisterName name1, RegisterName name2)
{
assert(name1 != name2);
return edges->test(name1 * rangeCount + name2);
}
#ifdef DEBUG_LOG
template <class RegisterPressure>
void LiveRangeGraph<RegisterPressure>::printPretty(LogModuleObject log)
{
UT_OBJECTLOG(log, PR_LOG_ALWAYS, ("Live ranges graph:\n"));
for (RegisterName name1 = RegisterName(1); name1 < rangeCount; name1 = RegisterName(name1 + 1)) {
UT_OBJECTLOG(log, PR_LOG_ALWAYS, ("\t%d:\t", name1));
for (RegisterName name2 = RegisterName(1); name2 < rangeCount; name2 = RegisterName(name2 + 1))
UT_OBJECTLOG(log, PR_LOG_ALWAYS, ("%c", ((name1 != name2) && haveEdge(name1, name2)) ? '1' : '0'));
UT_OBJECTLOG(log, PR_LOG_ALWAYS, ("\n"));
}
}
#endif // DEBUG_LOG
#endif // _LIVE_RANGE_GRAPH_

View File

@@ -1,4 +1,4 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
/* -*- 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.0 (the "NPL"); you may not use this file except in
@@ -12,17 +12,10 @@
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
package org.mozilla.javascript;
#include "Fundamentals.h"
#include "Liveness.h"
/**
* Objects that can wrap other values for reflection in the JS environment.
*/
public interface Wrapper {
public Object unwrap();
}

View File

@@ -0,0 +1,301 @@
/* -*- 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#ifndef _LIVENESS_H_
#define _LIVENESS_H_
#include "Fundamentals.h"
#include "ControlGraph.h"
#include "ControlNodes.h"
#include "Instruction.h"
#include "RegisterTypes.h"
// ----------------------------------------------------------------------------
// LivenessInfo -
template <class RegisterPressure>
struct LivenessInfo
{
RegisterPressure::Set* liveIn;
RegisterPressure::Set* liveOut;
DEBUG_LOG_ONLY(Uint32 size);
#ifdef DEBUG_LOG
void printPretty(LogModuleObject log);
#endif // DEBUG_LOG
};
// ----------------------------------------------------------------------------
// Liveness
//
// The liveness is defined by the following data-flow equations:
//
// LiveIn(n) = LocalLive(n) U (LiveOut(n) - Killed(n)).
// LiveOut(n) = U LiveIn(s) (s a successor of n).
//
// where LocalLive(n) is the set of used registers in the block n, Killed(n)
// is the set of defined registers in the block n, LiveIn(n) is the set of
// live registers at the begining of the block n and LiveOut(n) is the set
// of live registers at the end of the block n.
//
//
// We will compute the liveness analysis in two stages:
//
// 1- Build LocalLive(n) (wich is an approximation of LiveIn(n)) and Killed(n)
// for each block n.
// 2- Perform a backward data-flow analysis to propagate the liveness information
// through the entire control-flow graph.
//
template <class RegisterPressure>
struct Liveness
{
static LivenessInfo<RegisterPressure> analysis(ControlGraph& controlGraph, Uint32 rangeCount, const RegisterName* name2range);
static LivenessInfo<RegisterPressure> analysis(ControlGraph& controlGraph, Uint32 nameCount);
};
template <class RegisterPressure>
LivenessInfo<RegisterPressure> Liveness<RegisterPressure>::analysis(ControlGraph& controlGraph, Uint32 rangeCount, const RegisterName* name2range)
{
Pool& pool = controlGraph.pool;
ControlNode** nodes = controlGraph.dfsList;
Uint32 nNodes = controlGraph.nNodes;
// Allocate the temporary sets.
RegisterPressure::Set* killed = new(pool) RegisterPressure::Set[nNodes](pool, rangeCount);
// Allocate the globals sets.
RegisterPressure::Set* liveIn = new(pool) RegisterPressure::Set[nNodes](pool, rangeCount);
RegisterPressure::Set* liveOut = new(pool) RegisterPressure::Set[nNodes](pool, rangeCount);
// First stage of the liveness analysis: Compute the sets LocalLive(stored in LiveIn) and Killed.
//
for (Uint32 n = 0; n < (nNodes - 1); n++) {
ControlNode& node = *nodes[n];
RegisterPressure::Set& currentLocalLive = liveIn[n];
RegisterPressure::Set& currentKilled = killed[n];
// Find the instructions contributions to the sets LocalLive and Killed.
//
InstructionList& instructions = node.getInstructions();
for (InstructionList::iterator i = instructions.begin(); !instructions.done(i); i = instructions.advance(i)) {
Instruction& instruction = instructions.get(i);
// If a VirtualRegister is 'used' before being 'defined' then we add it to set LocalLive.
InstructionUse* useEnd = instruction.getInstructionUseEnd();
for (InstructionUse* usePtr = instruction.getInstructionUseBegin(); usePtr < useEnd; usePtr++)
if (usePtr->isRegister()) {
Uint32 index = name2range[usePtr->getRegisterName()];
if (!currentKilled.test(index))
currentLocalLive.set(index);
}
// If a Virtualregister is 'defined' then we add it to the set Killed.
InstructionDefine* defineEnd = instruction.getInstructionDefineEnd();
for (InstructionDefine* definePtr = instruction.getInstructionDefineBegin(); definePtr < defineEnd; definePtr++)
if (definePtr->isRegister())
currentKilled.set(name2range[definePtr->getRegisterName()]);
}
}
// Second stage of the liveness analysis: We propagate the LiveIn & LiveOut through the entire
// control-flow graph.
//
RegisterPressure::Set temp(pool, rangeCount);
bool changed;
do {
changed = false;
// For all nodes is this graph except the endNode.
for (Int32 n = (nNodes - 2); n >= 0; n--) {
ControlNode& node = *nodes[n];
RegisterPressure::Set& currentLiveIn = liveIn[n];
RegisterPressure::Set& currentLiveOut = liveOut[n];
// Compute temp = Union of LiveIn(s) (s a successor of this node) | usedByPhiNodes(n).
// temp will be the new LiveOut(n).
Uint32 nSuccessors = node.nSuccessors();
if (nSuccessors != 0) {
temp = liveIn[node.nthSuccessor(0).getTarget().dfsNum];
for (Uint32 s = 1; s < nSuccessors; s++)
temp |= liveIn[node.nthSuccessor(s).getTarget().dfsNum];
} else
temp.clear();
// If temp and LiveOut(n) differ then set LiveOut(n) = temp and recalculate the
// new LiveIn(n).
if (currentLiveOut != temp) {
currentLiveOut = temp;
temp -= killed[n]; // FIX: could be optimized with one call to unionDiff !
temp |= currentLiveIn;
if (currentLiveIn != temp) {
currentLiveIn = temp;
changed = true;
}
}
}
} while(changed);
LivenessInfo<RegisterPressure> liveness;
liveness.liveIn = liveIn;
liveness.liveOut = liveOut;
DEBUG_LOG_ONLY(liveness.size = nNodes);
return liveness;
}
template <class RegisterPressure>
LivenessInfo<RegisterPressure> Liveness<RegisterPressure>::analysis(ControlGraph& controlGraph, Uint32 nameCount)
{
Pool& pool = controlGraph.pool;
ControlNode** nodes = controlGraph.dfsList;
Uint32 nNodes = controlGraph.nNodes;
// Allocate the temporary sets.
RegisterPressure::Set* killed = new(pool) RegisterPressure::Set[nNodes](pool, nameCount);
RegisterPressure::Set* usedByPhiNodes = NULL;
// Allocate the globals sets.
RegisterPressure::Set* liveIn = new(pool) RegisterPressure::Set[nNodes](pool, nameCount);
RegisterPressure::Set* liveOut = new(pool) RegisterPressure::Set[nNodes](pool, nameCount);
// First stage of the liveness analysis: Compute the sets LocalLive(stored in LiveIn) and Killed.
//
for (Uint32 n = 0; n < (nNodes - 1); n++) {
ControlNode& node = *nodes[n];
RegisterPressure::Set& currentLocalLive = liveIn[n];
RegisterPressure::Set& currentKilled = killed[n];
InstructionList& phiNodes = node.getPhiNodeInstructions();
if ((usedByPhiNodes == NULL) && !phiNodes.empty())
usedByPhiNodes = new(pool) RegisterPressure::Set[nNodes](pool, nameCount);
for (InstructionList::iterator p = phiNodes.begin(); !phiNodes.done(p); p = phiNodes.advance(p)) {
Instruction& phiNode = phiNodes.get(p);
InstructionDefine& define = phiNode.getInstructionDefineBegin()[0];
currentKilled.set(define.getRegisterName());
typedef DoublyLinkedList<ControlEdge> ControlEdgeList;
const ControlEdgeList& predecessors = node.getPredecessors();
ControlEdgeList::iterator p = predecessors.begin();
InstructionUse* useEnd = phiNode.getInstructionUseEnd();
for (InstructionUse* usePtr = phiNode.getInstructionUseBegin(); usePtr < useEnd; usePtr++, p = predecessors.advance(p))
if (usePtr->isRegister())
usedByPhiNodes[predecessors.get(p).getSource().dfsNum].set(usePtr->getRegisterName());
}
// Find the instructions contributions to the sets LocalLive and Killed.
//
InstructionList& instructions = node.getInstructions();
for (InstructionList::iterator i = instructions.begin(); !instructions.done(i); i = instructions.advance(i)) {
Instruction& instruction = instructions.get(i);
// If a VirtualRegister is 'used' before being 'defined' then we add it to set LocalLive.
InstructionUse* useEnd = instruction.getInstructionUseEnd();
for (InstructionUse* usePtr = instruction.getInstructionUseBegin(); usePtr < useEnd; usePtr++)
if (usePtr->isRegister()) {
Uint32 index = usePtr->getRegisterName();
if (!currentKilled.test(index))
currentLocalLive.set(index);
}
// If a Virtualregister is 'defined' then we add it to the set Killed.
InstructionDefine* defineEnd = instruction.getInstructionDefineEnd();
for (InstructionDefine* definePtr = instruction.getInstructionDefineBegin(); definePtr < defineEnd; definePtr++)
if (definePtr->isRegister())
currentKilled.set(definePtr->getRegisterName());
}
}
// Second stage of the liveness analysis: We propagate the LiveIn & LiveOut through the entire
// control-flow graph.
//
RegisterPressure::Set temp(pool, nameCount);
bool changed;
do {
changed = false;
// For all nodes is this graph except the endNode.
for (Int32 n = (nNodes - 2); n >= 0; n--) {
ControlNode& node = *nodes[n];
RegisterPressure::Set& currentLiveIn = liveIn[n];
RegisterPressure::Set& currentLiveOut = liveOut[n];
// Compute temp = Union of LiveIn(s) (s a successor of this node) | usedByPhiNodes(n).
// temp will be the new LiveOut(n).
Uint32 nSuccessors = node.nSuccessors();
if (nSuccessors != 0) {
temp = liveIn[node.nthSuccessor(0).getTarget().dfsNum];
for (Uint32 s = 1; s < nSuccessors; s++)
temp |= liveIn[node.nthSuccessor(s).getTarget().dfsNum];
} else
temp.clear();
// Insert the phiNodes contribution.
if (usedByPhiNodes != NULL)
temp |= usedByPhiNodes[n];
// If temp and LiveOut(n) differ then set LiveOut(n) = temp and recalculate the
// new LiveIn(n).
if (currentLiveOut != temp) {
currentLiveOut = temp;
temp -= killed[n]; // FIX: could be optimized with one call to unionDiff !
temp |= currentLiveIn;
if (currentLiveIn != temp) {
currentLiveIn = temp;
changed = true;
}
}
}
} while(changed);
LivenessInfo<RegisterPressure> liveness;
liveness.liveIn = liveIn;
liveness.liveOut = liveOut;
DEBUG_LOG_ONLY(liveness.size = nNodes);
return liveness;
}
#ifdef DEBUG_LOG
template <class RegisterPressure>
void LivenessInfo<RegisterPressure>::printPretty(LogModuleObject log)
{
for (Uint32 n = 0; n < size; n++) {
UT_OBJECTLOG(log, PR_LOG_ALWAYS, ("Node N%d:\n\tliveIn = ", n));
liveIn[n].printPretty(log);
UT_OBJECTLOG(log, PR_LOG_ALWAYS, ("\tliveOut = "));
liveOut[n].printPretty(log);
UT_OBJECTLOG(log, PR_LOG_ALWAYS, ("\n"));
}
}
#endif // DEBUG_LOG
#endif // _LIVENESS_H_

View File

@@ -0,0 +1,40 @@
#! gmake
DEPTH = ../..
MODULE_NAME = RegisterAllocator
include $(DEPTH)/config/config.mk
INCLUDES += \
-I$(DEPTH)/Utilities/General \
-I$(DEPTH)/Utilities/zlib \
-I$(DEPTH)/Runtime/ClassReader \
-I$(DEPTH)/Runtime/NativeMethods \
-I$(DEPTH)/Runtime/System \
-I$(DEPTH)/Runtime/ClassInfo \
-I$(DEPTH)/Runtime/FileReader \
-I$(DEPTH)/Compiler/PrimitiveGraph \
-I$(DEPTH)/Compiler/FrontEnd \
-I$(DEPTH)/Compiler/Optimizer \
-I$(DEPTH)/Compiler/CodeGenerator \
-I$(DEPTH)/Compiler/CodeGenerator/md \
-I$(DEPTH)/Compiler/CodeGenerator/md/$(CPU_ARCH) \
-I$(DEPTH)/Compiler/RegisterAllocator \
-I$(DEPTH)/Driver/StandAloneJava \
-I$(DEPTH)/Debugger \
$(NULL)
CXXSRCS = \
RegisterAllocator.cpp \
RegisterAllocatorTools.cpp \
DominatorGraph.cpp \
VirtualRegister.cpp \
BitSet.cpp \
SparseSet.cpp \
$(NULL)
include $(DEPTH)/config/rules.mk
libs:: $(MODULE)

View File

@@ -0,0 +1,392 @@
/* -*- 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#ifndef _PHI_NODE_REMOVER_H_
#define _PHI_NODE_REMOVER_H_
#include "Fundamentals.h"
#include "Pool.h"
#include "ControlGraph.h"
#include "DominatorGraph.h"
#include "VirtualRegister.h"
#include "RegisterPressure.h"
#include "Liveness.h"
#include "Instruction.h"
#include "InstructionEmitter.h"
#include "SparseSet.h"
#include <string.h>
//------------------------------------------------------------------------------
// RegisterNameNode -
struct RegisterNameNode
{
RegisterNameNode* next;
RegisterName newName;
Uint32 nextPushed;
};
//------------------------------------------------------------------------------
// CopyData -
struct CopyData
{
RegisterName source;
RegisterClassKind classKind;
Uint32 useCount;
bool isLiveOut;
RegisterName sourceNameToUse;
RegisterName temporaryName;
RegisterNameNode* newName;
};
//------------------------------------------------------------------------------
// PhiNodeRemover<RegisterPressure> -
template <class RegisterPressure>
struct PhiNodeRemover
{
// Replace the phi nodes by copy instructions.
static void replacePhiNodes(ControlGraph& controlGraph, VirtualRegisterManager& vrManager, InstructionEmitter& emitter);
};
// Split some of the critical edges and return true if there are still some
// in the graph after that.
//
static bool splitCriticalEdges(ControlGraph& /*cg*/)
{
// FIX: not implemented.
return true;
}
inline void pushName(Pool& pool, RegisterNameNode** stack, SparseSet& pushed, Uint32* nodeListPointer, RegisterName oldName, RegisterName newName)
{
RegisterNameNode& newNode = *new(pool) RegisterNameNode();
if (pushed.test(oldName))
(*stack)->newName = newName;
else {
newNode.newName = newName;
newNode.nextPushed = *nodeListPointer;
*nodeListPointer = oldName;
newNode.next = *stack;
*stack = &newNode;
pushed.set(oldName);
}
}
template <class RegisterPressure>
void PhiNodeRemover<RegisterPressure>::replacePhiNodes(ControlGraph& controlGraph, VirtualRegisterManager& vrManager, InstructionEmitter& emitter)
{
Pool& pool = controlGraph.pool;
ControlNode** nodes = controlGraph.dfsList;
Uint32 nNodes = controlGraph.nNodes;
// Initialize the local variables.
//
// When we insert the copies we will also need to create new VirtualRegisters for
// the insertion of temporaries. The maximum number of temporary register will not
// exceed the number of phiNodes in the primitive graph.
Uint32 nameCount = vrManager.getSize();
Uint32 maxNameCount = nameCount;
for (Uint32 n = 0; n < nNodes; n++)
maxNameCount += nodes[n]->getPhiNodes().length();
// If the CFG contains some critical edges (backward edge which source has more than one
// outgoing edge and destination has more than one incomimg edge) then we need the liveness
// information to be able to insert temporary copies.
RegisterPressure::Set* liveOut = NULL;
if (splitCriticalEdges(controlGraph))
liveOut = Liveness<LowRegisterPressure>::analysis(controlGraph, nameCount).liveOut;
DominatorGraph dGraph(controlGraph);
SparseSet pushed(pool, maxNameCount);
SparseSet destinationList(pool, maxNameCount);
SparseSet workList(pool, maxNameCount);
CopyData* copyStats = new(pool) CopyData[maxNameCount];
memset(copyStats, '\0', maxNameCount*sizeof(CopyData));
struct NodeStack {
Uint32* next;
Uint32* limit;
Uint32 pushedList;
};
// Allocate the node stack and initialize the node stack pointer.
NodeStack* nodeStack = new(pool) NodeStack[nNodes + 1];
NodeStack* nodeStackPtr = nodeStack;
// We start by the begin node.
Uint32 startNode = 0;
Uint32* next = &startNode;
Uint32* limit = &startNode + 1;
while (true) {
if (next == limit) {
// If there are no more node in the sibling, we have to pop the current
// frame from the stack and update the copyStats of the pushed nodes.
//
if (nodeStackPtr == nodeStack)
// We are at the bottom of the stack and there are no more nodes
// to look at. We are done !
break;
--nodeStackPtr;
// We are done with all the children of this node in the dominator tree.
// We need to update the copy information of all the new names pushed
// during the walk over this node.
Uint32 pushedList = nodeStackPtr->pushedList;
while (pushedList != 0) {
Uint32 nextName = copyStats[pushedList].newName->nextPushed;
copyStats[pushedList].newName = copyStats[pushedList].newName->next;
pushedList = nextName;
}
// restore the previous frame.
next = nodeStackPtr->next;
limit = nodeStackPtr->limit;
} else {
Uint32 currentNode = *next++;
Uint32 pushedList = 0;
// Initialize the sets.
pushed.clear();
destinationList.clear();
// STEP1:
// Walk the instruction list and to replace all the instruction uses with their new name.
// If the instruction is a phi node and its defined register is alive at the end of this
// block then we push the defined register into the stack.
//
ControlNode& node = *nodes[currentNode];
RegisterPressure::Set* currentLiveOut = (liveOut != NULL) ? &liveOut[currentNode] : (RegisterPressure::Set*) 0;
InstructionList& phiNodes = node.getPhiNodeInstructions();
for (InstructionList::iterator p = phiNodes.begin(); !phiNodes.done(p); p = phiNodes.advance(p)) {
Instruction& phiNode = phiNodes.get(p);
InstructionUse* useEnd = phiNode.getInstructionUseEnd();
for (InstructionUse* usePtr = phiNode.getInstructionUseBegin(); usePtr < useEnd; usePtr++) {
assert(usePtr->isRegister());
RegisterName name = usePtr->getRegisterName();
if (copyStats[name].newName != NULL && copyStats[name].newName->newName != name)
usePtr->setRegisterName(copyStats[name].newName->newName);
}
if (currentLiveOut != NULL) {
// This is a phi node and we have to push its defined name if it is live
// at the end of the node. We only need to do this if the CFG has critical edges.
assert(phiNode.getInstructionDefineBegin() != phiNode.getInstructionDefineEnd() && phiNode.getInstructionDefineBegin()[0].isRegister());
RegisterName name = phiNode.getInstructionDefineBegin()[0].getRegisterName();
if (currentLiveOut->test(name))
pushName(pool, &(copyStats[name].newName), pushed, &pushedList, name, name);
}
}
InstructionList& instructions = node.getInstructions();
for (InstructionList::iterator i = instructions.begin(); !instructions.done(i); i = instructions.advance(i)) {
Instruction& instruction = instructions.get(i);
InstructionUse* useEnd = instruction.getInstructionUseEnd();
for (InstructionUse* usePtr = instruction.getInstructionUseBegin(); usePtr < useEnd; usePtr++)
if (usePtr->isRegister()) {
RegisterName name = usePtr->getRegisterName();
if (copyStats[name].newName != NULL && copyStats[name].newName->newName != name)
usePtr->setRegisterName(copyStats[name].newName->newName);
}
}
// STEP2:
// Look at this node's successors' phiNodes. We keep track of the number of time
// a VR will be used by another copy instruction and insert each definition into the
// destinationList. This is the only pass over this node's successors as we will
// get all the information we need in the CopyData structures.
//
ControlEdge* successorEdgeEnd = node.getSuccessorsEnd();
for (ControlEdge* successorEdgePtr = node.getSuccessorsBegin(); successorEdgePtr < successorEdgeEnd; successorEdgePtr++) {
Uint32 useIndex = successorEdgePtr->getIndex();
ControlNode& successor = successorEdgePtr->getTarget();
// Look at its phi nodes. The phi nodes are at the top of the instruction list. We exit
// as soon as we find an instruction which is not a phi node
InstructionList& phiNodes = successor.getPhiNodeInstructions();
for (InstructionList::iterator p = phiNodes.begin(); !phiNodes.done(p); p = phiNodes.advance(p)) {
Instruction& phiNode = phiNodes.get(p);
assert((phiNode.getInstructionUseBegin() + useIndex) < phiNode.getInstructionUseEnd());
assert(phiNode.getInstructionDefineBegin() != phiNode.getInstructionDefineEnd());
InstructionUse& source = phiNode.getInstructionUseBegin()[useIndex];
InstructionDefine& destination = phiNode.getInstructionDefineBegin()[0];
assert(source.isRegister() && destination.isRegister());
RegisterName sourceName = source.getRegisterName();
RegisterName destinationName = destination.getRegisterName();
// Get the correct name for the source.
if (copyStats[sourceName].newName != NULL)
sourceName = copyStats[sourceName].newName->newName;
// Update the CopyData structures.
if ((sourceName != rnInvalid) && (sourceName != destinationName)) {
copyStats[destinationName].source = sourceName;
copyStats[destinationName].classKind = destination.getRegisterClass();
copyStats[destinationName].isLiveOut = (currentLiveOut != NULL) ? currentLiveOut->test(destinationName) : false;
copyStats[destinationName].sourceNameToUse = destinationName;
copyStats[sourceName].sourceNameToUse = sourceName;
copyStats[sourceName].useCount++;
destinationList.set(destinationName);
}
}
}
// STEP3:
// Insert into the worklist only the destination registers that will be not used in
// another copy instruction in this block.
//
assert(workList.getSize() == 0);
for (SparseSet::iterator d = destinationList.begin(); !destinationList.done(d); d = destinationList.advance(d)) {
Uint32 dest = destinationList.get(d);
if (copyStats[dest].useCount == 0)
workList.set(dest);
}
// STEP4:
// Insert the copy instructions.
//
Uint32 destinationListSize = destinationList.getSize();
InstructionList::iterator endOfTheNode = instructions.end();
// Find the right place to insert the copy instructions.
if (destinationListSize != 0)
while (instructions.get(endOfTheNode).getFlags() & ifControl)
endOfTheNode = instructions.retreat(endOfTheNode);
while (destinationListSize != 0) {
while(workList.getSize()) {
RegisterName destinationName = RegisterName(workList.getOne());
RegisterName sourceName = copyStats[destinationName].source;
workList.clear(destinationName);
if (copyStats[destinationName].isLiveOut && !copyStats[destinationName].temporaryName) {
// Lost copy problem.
copyStats[destinationName].isLiveOut = false;
RegisterName sourceName = destinationName;
RegisterClassKind classKind = copyStats[sourceName].classKind;
RegisterName destinationName = getName(vrManager.newVirtualRegister(classKind));
assert(destinationName < maxNameCount);
copyStats[destinationName].classKind = classKind;
copyStats[sourceName].useCount = 0;
// We need to insert a copy to a temporary register to keep the
// source register valid at the end of the node defining it.
// This copy will be inserted right after the phi node defining it.
RegisterName from = copyStats[sourceName].sourceNameToUse;
Instruction* definingPhiNode = vrManager.getVirtualRegister(from).getDefiningInstruction();
assert(definingPhiNode && (definingPhiNode->getFlags() & ifPhiNode) != 0);
RegisterID fromID = buildRegisterID(from, classKind);
RegisterID toID = buildRegisterID(destinationName, classKind);
Instruction& copy = emitter.newCopy(*definingPhiNode->getPrimitive(), fromID, toID);
vrManager.getVirtualRegister(destinationName).setDefiningInstruction(copy);
definingPhiNode->getPrimitive()->getContainer()->getInstructions().addFirst(copy);
copyStats[sourceName].temporaryName = destinationName;
copyStats[sourceName].sourceNameToUse = destinationName;
pushName(pool, &(copyStats[sourceName].newName), pushed, &pushedList, sourceName, destinationName);
}
// Insert the copy instruction at the end of the current node.
RegisterName from = copyStats[sourceName].sourceNameToUse;
RegisterClassKind classKind = copyStats[destinationName].classKind;
RegisterID fromID = buildRegisterID(from, classKind);
RegisterID toID = buildRegisterID(destinationName, classKind);
Instruction& copy = emitter.newCopy(*vrManager.getVirtualRegister(from).getDefiningInstruction()->getPrimitive(), fromID, toID);
instructions.insertAfter(copy, endOfTheNode);
endOfTheNode = instructions.advance(endOfTheNode);
copyStats[sourceName].useCount = 0;
if (destinationList.test(sourceName) && copyStats[sourceName].isLiveOut)
pushName(pool, &(copyStats[sourceName].newName), pushed, &pushedList, sourceName, destinationName);
copyStats[sourceName].isLiveOut = false;
copyStats[sourceName].sourceNameToUse = destinationName;
if (destinationList.test(sourceName))
workList.set(sourceName);
destinationList.clear(destinationName);
}
destinationListSize = destinationList.getSize();
if (destinationListSize != 0) {
RegisterName sourceName = RegisterName(destinationList.getOne());
RegisterName destinationName;
if (!copyStats[sourceName].temporaryName) {
// Cycle problem.
RegisterClassKind classKind = copyStats[sourceName].classKind;
destinationName = getName(vrManager.newVirtualRegister(classKind));
assert(destinationName < maxNameCount);
copyStats[destinationName].classKind = classKind;
copyStats[sourceName].temporaryName = destinationName;
// Insert the copy instruction at the end of the current node.
RegisterName from = copyStats[sourceName].sourceNameToUse;
RegisterID fromID = buildRegisterID(from, classKind);
RegisterID toID = buildRegisterID(destinationName, classKind);
Instruction& copy = emitter.newCopy(*vrManager.getVirtualRegister(from).getDefiningInstruction()->getPrimitive(), fromID, toID);
vrManager.getVirtualRegister(destinationName).setDefiningInstruction(copy);
instructions.insertAfter(copy, endOfTheNode);
endOfTheNode = instructions.advance(endOfTheNode);
} else
destinationName = copyStats[sourceName].temporaryName;
copyStats[sourceName].useCount = 0;
copyStats[sourceName].isLiveOut = false;
copyStats[sourceName].sourceNameToUse = destinationName;
pushName(pool, &(copyStats[sourceName].newName), pushed, &pushedList, sourceName, destinationName);
workList.set(sourceName);
}
}
nodeStackPtr->pushedList = pushedList;
nodeStackPtr->next = next;
nodeStackPtr->limit = limit;
++nodeStackPtr;
next = dGraph.getSuccessorsBegin(currentNode);
limit = dGraph.getSuccessorsEnd(currentNode);
}
}
}
#endif // _PHI_NODE_REMOVER_H_

View File

@@ -0,0 +1,155 @@
/* -*- 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "Fundamentals.h"
#include "LogModule.h"
#include "RegisterAllocator.h"
#include "RegisterPressure.h"
#include "RegisterAllocatorTools.h"
#include "PhiNodeRemover.h"
#include "LiveRange.h"
#include "Liveness.h"
#include "InterferenceGraph.h"
#include "LiveRangeGraph.h"
#include "Coalescing.h"
#include "Spilling.h"
#include "Coloring.h"
#include "Splits.h"
class Pool;
class ControlGraph;
class VirtualRegisterManager;
class InstructionEmitter;
UT_DEFINE_LOG_MODULE(RegAlloc);
void RegisterAllocator::allocateRegisters(Pool& pool, ControlGraph& controlGraph, VirtualRegisterManager& vrManager, InstructionEmitter& emitter)
{
// Insert the phi node instructions. We want to do this to have a single defined register per instruction.
// If we keep the PhiNode (as a DataNode) and a PhiNode is of DoubleWordKind then we have to execute
// some special code for the high word annotation.
//
RegisterAllocatorTools::insertPhiNodeInstructions(controlGraph, emitter);
// Perform some tests on the instruction graph.
//
DEBUG_ONLY(RegisterAllocatorTools::testTheInstructionGraph(controlGraph, vrManager));
// Replace the phi node instructions by their equivalent copy instructions.
//
PhiNodeRemover<LowRegisterPressure>::replacePhiNodes(controlGraph, vrManager, emitter);
// Do the register allocation.
//
RegisterAllocator registerAllocator(pool, controlGraph, vrManager, emitter);
registerAllocator.doGraphColoring();
}
void RegisterAllocator::doGraphColoring()
{
// Initialize the liverange map.
//
initLiveRanges();
// Build the live ranges. We do this to compress the number of RegisterNames
// used in the insterference graph.
//
LiveRange<LowRegisterPressure>::build(*this);
// Remove unnecessary copies.
//
RegisterAllocatorTools::removeUnnecessaryCopies(*this);
for (Uint8 loop = 0; loop < 10; loop++) {
UT_OBJECTLOG(UT_LOG_MODULE(RegAlloc), PR_LOG_ALWAYS, ("********* RegisterAllocator loop %d *********\n", loop));
while(true) {
// Build the interference graph.
//
iGraph.build();
// Coalesce the copy instructions.
//
if (!Coalescing<LowRegisterPressure>::coalesce(*this))
break;
}
// Print the interference graph.
//
DEBUG_LOG_ONLY(iGraph.printPretty(UT_LOG_MODULE(RegAlloc)));
// Calculate the spill costs.
//
Spilling<LowRegisterPressure>::calculateSpillCosts(*this);
DEBUG_LOG_ONLY(RegisterAllocatorTools::printSpillCosts(*this));
// Calculate the split costs.
//
Splits<LowRegisterPressure>::calculateSplitCosts(*this);
DEBUG_LOG_ONLY(RegisterAllocatorTools::printSplitCosts(*this));
// Build the live range graph.
//
lGraph.build();
DEBUG_LOG_ONLY(lGraph.printPretty(UT_LOG_MODULE(RegAlloc)));
// Color the graph. If it succeeds then we're done with the
// register allocation.
//
if (Coloring<LowRegisterPressure>::color(*this)) {
// Write the final colors in the instruction graph.
//
Coloring<LowRegisterPressure>::finalColoring(*this);
UT_OBJECTLOG(UT_LOG_MODULE(RegAlloc), PR_LOG_ALWAYS, ("********** RegisterAllocator done **********\n"));
DEBUG_LOG_ONLY(RegisterAllocatorTools::printInstructions(*this));
return;
}
// We need to spill some registers.
//
Spilling<LowRegisterPressure>::insertSpillCode(*this);
// Insert the split instructions.
//
Splits<LowRegisterPressure>::insertSplitCode(*this);
// Update the live ranges.
//
// FIX
}
#ifdef DEBUG_LOG
RegisterAllocatorTools::updateInstructionGraph(*this);
RegisterAllocatorTools::printInstructions(*this);
#endif
fprintf(stderr, "!!! Coloring failed after 10 loops !!!\n");
abort();
}
void RegisterAllocator::initLiveRanges()
{
Uint32 count = this->nameCount;
RegisterName* name2range = new(pool) RegisterName[nameCount];
for (RegisterName r = RegisterName(1); r < count; r = RegisterName(r + 1))
name2range[r] = r;
this->name2range = name2range;
rangeCount = count;
}

View File

@@ -0,0 +1,88 @@
/* -*- 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#ifndef _REGISTER_ALLOCATOR_H_
#define _REGISTER_ALLOCATOR_H_
class Pool;
class ControlGraph;
class InstructionEmitter;
struct SpillCost;
struct SplitCost;
#include "Liveness.h"
#include "VirtualRegister.h"
#include "RegisterPressure.h" // This should included by Backend.cpp
#include "InterferenceGraph.h"
#include "LiveRangeGraph.h"
//template <class RegisterPressure>
class RegisterAllocator
{
public:
Pool& pool; //
ControlGraph& controlGraph; //
VirtualRegisterManager& vrManager; //
InstructionEmitter& emitter; //
RegisterName* name2range; //
RegisterName* color; //
SpillCost* spillCost; //
SparseSet* willSpill; //
SplitCost* splitCost; //
NameLinkedList** splitAround; //
InterferenceGraph<LowRegisterPressure> iGraph; //
LiveRangeGraph<LowRegisterPressure> lGraph; //
LivenessInfo<LowRegisterPressure> liveness; //
Uint32 nameCount; //
Uint32 rangeCount; //
bool splitFound; //
private:
//
//
void doGraphColoring();
public:
//
//
inline RegisterAllocator(Pool& pool, ControlGraph& controlGraph, VirtualRegisterManager& vrManager, InstructionEmitter& emitter);
//
//
bool canInterfere(RegisterName /*name1*/, RegisterName /*name2*/) const {return true;}
//
//
void initLiveRanges();
//
//
static void allocateRegisters(Pool& pool, ControlGraph& controlGraph, VirtualRegisterManager& vrManager, InstructionEmitter& emitter);
};
//
//
inline RegisterAllocator::RegisterAllocator(Pool& pool, ControlGraph& controlGraph, VirtualRegisterManager& vrManager, InstructionEmitter& emitter)
: pool(pool), controlGraph(controlGraph), vrManager(vrManager), emitter(emitter), iGraph(*this), lGraph(*this), nameCount(vrManager.getSize()) {}
#endif // _REGISTER_ALLOCATOR_H_

View File

@@ -0,0 +1,355 @@
/* -*- 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "Fundamentals.h"
#include "LogModule.h"
#include "RegisterAllocatorTools.h"
#include "Pool.h"
#include "ControlGraph.h"
#include "ControlNodes.h"
#include "Primitives.h"
#include "InstructionEmitter.h"
#include "Instruction.h"
#include "RegisterAllocator.h"
#include "Spilling.h"
#include "Splits.h"
#include "BitSet.h"
UT_EXTERN_LOG_MODULE(RegAlloc);
#ifdef DEBUG
void RegisterAllocatorTools::testTheInstructionGraph(ControlGraph& controlGraph, VirtualRegisterManager& vrManager)
{
// Test the declared VirtualRegisters. The register allocator tries to condense the register universe.
// Any gap in the VirtualRegister names will be a loss of efficiency !!!!
Uint32 nameCount = vrManager.getSize();
BitSet registerSeen(controlGraph.pool, nameCount);
ControlNode** nodes = controlGraph.dfsList;
Uint32 nNodes = controlGraph.nNodes;
for (Uint32 n = 0; n < nNodes; n++) {
InstructionList& instructions = nodes[n]->getInstructions();
for (InstructionList::iterator i = instructions.begin(); !instructions.done(i); i = instructions.advance(i)) {
Instruction& instruction = instructions.get(i);
InstructionUse* useEnd = instruction.getInstructionUseEnd();
for (InstructionUse* usePtr = instruction.getInstructionUseBegin(); usePtr < useEnd; usePtr++)
if (usePtr->isRegister())
registerSeen.set(usePtr->getRegisterName());
InstructionDefine* defineEnd = instruction.getInstructionDefineEnd();
for (InstructionDefine* definePtr = instruction.getInstructionDefineBegin(); definePtr < defineEnd; definePtr++)
if (definePtr->isRegister())
registerSeen.set(definePtr->getRegisterName());
}
InstructionList& phiNodes = nodes[n]->getPhiNodeInstructions();
for (InstructionList::iterator p = phiNodes.begin(); !phiNodes.done(p); p = phiNodes.advance(p)) {
Instruction& instruction = phiNodes.get(p);
InstructionUse* useEnd = instruction.getInstructionUseEnd();
for (InstructionUse* usePtr = instruction.getInstructionUseBegin(); usePtr < useEnd; usePtr++)
if (usePtr->isRegister())
registerSeen.set(usePtr->getRegisterName());
InstructionDefine* defineEnd = instruction.getInstructionDefineEnd();
for (InstructionDefine* definePtr = instruction.getInstructionDefineBegin(); definePtr < defineEnd; definePtr++)
if (definePtr->isRegister())
registerSeen.set(definePtr->getRegisterName());
}
}
bool renameRegisters = false;
for (BitSet::iterator i = registerSeen.nextZero(0); !registerSeen.done(i); i = registerSeen.nextZero(i)) {
renameRegisters = true;
fprintf(stderr,
"WARNING: The VirtualRegister vr%d has been allocated during CodeGeneration but\n"
" is never used nor defined by any instruction in the instruction graph\n"
" PLEASE FIX \n",
i);
}
if (renameRegisters) {
Instruction** definingInstruction = new Instruction*[nameCount];
memset(definingInstruction, '\0', nameCount * sizeof(Instruction*));
RegisterName* newName = new RegisterName[nameCount];
memset(newName, '\0', nameCount * sizeof(RegisterName));
RegisterName nextName = RegisterName(1);
for (Uint32 n = 0; n < nNodes; n++) {
InstructionList& instructions = nodes[n]->getInstructions();
for (InstructionList::iterator i = instructions.begin(); !instructions.done(i); i = instructions.advance(i)) {
Instruction& instruction = instructions.get(i);
InstructionUse* useEnd = instruction.getInstructionUseEnd();
for (InstructionUse* usePtr = instruction.getInstructionUseBegin(); usePtr < useEnd; usePtr++)
if (usePtr->isRegister()) {
RegisterName name = usePtr->getRegisterName();
if (newName[name] == rnInvalid) {
newName[name] = nextName;
definingInstruction[nextName] = vrManager.getVirtualRegister(name).getDefiningInstruction();
nextName = RegisterName(nextName + 1);
}
usePtr->setRegisterName(newName[name]);
}
InstructionDefine* defineEnd = instruction.getInstructionDefineEnd();
for (InstructionDefine* definePtr = instruction.getInstructionDefineBegin(); definePtr < defineEnd; definePtr++)
if (definePtr->isRegister()) {
RegisterName name = definePtr->getRegisterName();
if (newName[name] == rnInvalid) {
newName[name] = nextName;
definingInstruction[nextName] = vrManager.getVirtualRegister(name).getDefiningInstruction();
nextName = RegisterName(nextName + 1);
}
definePtr->setRegisterName(newName[name]);
}
}
InstructionList& phiNodes = nodes[n]->getPhiNodeInstructions();
for (InstructionList::iterator p = phiNodes.begin(); !phiNodes.done(p); p = phiNodes.advance(p)) {
Instruction& instruction = phiNodes.get(p);
InstructionUse* useEnd = instruction.getInstructionUseEnd();
for (InstructionUse* usePtr = instruction.getInstructionUseBegin(); usePtr < useEnd; usePtr++)
if (usePtr->isRegister()) {
RegisterName name = usePtr->getRegisterName();
if (newName[name] == rnInvalid) {
newName[name] = nextName;
definingInstruction[nextName] = vrManager.getVirtualRegister(name).getDefiningInstruction();
nextName = RegisterName(nextName + 1);
}
usePtr->setRegisterName(newName[name]);
}
InstructionDefine* defineEnd = instruction.getInstructionDefineEnd();
for (InstructionDefine* definePtr = instruction.getInstructionDefineBegin(); definePtr < defineEnd; definePtr++)
if (definePtr->isRegister()) {
RegisterName name = definePtr->getRegisterName();
if (newName[name] == rnInvalid) {
newName[name] = nextName;
definingInstruction[nextName] = vrManager.getVirtualRegister(name).getDefiningInstruction();
nextName = RegisterName(nextName + 1);
}
definePtr->setRegisterName(newName[name]);
}
}
}
vrManager.setSize(nextName);
for (RegisterName r = RegisterName(1); r < nextName; r = RegisterName(r + 1))
vrManager.getVirtualRegister(r).definingInstruction = definingInstruction[r];
UT_OBJECTLOG(UT_LOG_MODULE(RegAlloc), PR_LOG_ALWAYS, ("RegisterMap:\n"));
for (Uint32 i = 1; i < nameCount; i++)
if (newName[i] != 0)
UT_OBJECTLOG(UT_LOG_MODULE(RegAlloc), PR_LOG_ALWAYS, ("\tvr%d becomes vr%d.\n", i, newName[i]));
else
UT_OBJECTLOG(UT_LOG_MODULE(RegAlloc), PR_LOG_ALWAYS, ("\tvr%d is dead.\n", i));
delete newName;
delete definingInstruction;
}
}
#endif // DEBUG
void RegisterAllocatorTools::removeUnnecessaryCopies(RegisterAllocator& registerAllocator)
{
ControlGraph& controlGraph = registerAllocator.controlGraph;
ControlNode** nodes = controlGraph.dfsList;
Uint32 nNodes = controlGraph.nNodes;
RegisterName* name2range = registerAllocator.name2range;
for (Uint32 n = 0; n < nNodes; n++) {
InstructionList& instructions = nodes[n]->getInstructions();
for (InstructionList::iterator i = instructions.begin(); !instructions.done(i);) {
Instruction& instruction = instructions.get(i);
i = instructions.advance(i);
if (instruction.getFlags() & ifCopy) {
assert(instruction.getInstructionUseBegin() != instruction.getInstructionUseEnd() && instruction.getInstructionUseBegin()[0].isRegister());
assert(instruction.getInstructionDefineBegin() != instruction.getInstructionDefineEnd() && instruction.getInstructionDefineBegin()[0].isRegister());
RegisterName source = name2range[instruction.getInstructionUseBegin()[0].getRegisterName()];
RegisterName destination = name2range[instruction.getInstructionDefineBegin()[0].getRegisterName()];
if (source == destination)
instruction.remove();
}
}
}
}
void RegisterAllocatorTools::updateInstructionGraph(RegisterAllocator& registerAllocator)
{
ControlGraph& controlGraph = registerAllocator.controlGraph;
ControlNode** nodes = controlGraph.dfsList;
Uint32 nNodes = controlGraph.nNodes;
RegisterName* name2range = registerAllocator.name2range;
for (Uint32 n = 0; n < nNodes; n++) {
InstructionList& instructions = nodes[n]->getInstructions();
for (InstructionList::iterator i = instructions.begin(); !instructions.done(i); i = instructions.advance(i)) {
Instruction& instruction = instructions.get(i);
InstructionUse* useEnd = instruction.getInstructionUseEnd();
for (InstructionUse* usePtr = instruction.getInstructionUseBegin(); usePtr < useEnd; usePtr++)
if (usePtr->isRegister())
usePtr->setRegisterName(name2range[usePtr->getRegisterName()]);
InstructionDefine* defineEnd = instruction.getInstructionDefineEnd();
for (InstructionDefine* definePtr = instruction.getInstructionDefineBegin(); definePtr < defineEnd; definePtr++)
if (definePtr->isRegister())
definePtr->setRegisterName(name2range[definePtr->getRegisterName()]);
}
InstructionList& phiNodes = nodes[n]->getPhiNodeInstructions();
for (InstructionList::iterator p = phiNodes.begin(); !phiNodes.done(p); p = phiNodes.advance(p)) {
Instruction& instruction = phiNodes.get(p);
InstructionUse* useEnd = instruction.getInstructionUseEnd();
for (InstructionUse* usePtr = instruction.getInstructionUseBegin(); usePtr < useEnd; usePtr++)
if (usePtr->isRegister())
usePtr->setRegisterName(name2range[usePtr->getRegisterName()]);
InstructionDefine* defineEnd = instruction.getInstructionDefineEnd();
for (InstructionDefine* definePtr = instruction.getInstructionDefineBegin(); definePtr < defineEnd; definePtr++)
if (definePtr->isRegister())
definePtr->setRegisterName(name2range[definePtr->getRegisterName()]);
}
}
}
void RegisterAllocatorTools::insertPhiNodeInstructions(ControlGraph& controlGraph, InstructionEmitter& emitter)
{
Pool& pool = controlGraph.pool;
ControlNode** nodes = controlGraph.dfsList;
Uint32 nNodes = controlGraph.nNodes;
for (Uint32 n = 0; n < nNodes; n++) {
ControlNode& node = *nodes[n];
DoublyLinkedList<PhiNode>& phiNodes = node.getPhiNodes();
if (!phiNodes.empty()) {
// Set the index of the incoming edges.
Uint32 index = 0;
const DoublyLinkedList<ControlEdge>& predecessors = node.getPredecessors();
for (DoublyLinkedList<ControlEdge>::iterator p = predecessors.begin(); !predecessors.done(p); p = predecessors.advance(p))
predecessors.get(p).setIndex(index++);
// Insert the phi node instruction in the instruction list.
for (DoublyLinkedList<PhiNode>::iterator i = phiNodes.begin(); !phiNodes.done(i); i = phiNodes.advance(i)) {
PhiNode& phiNode = phiNodes.get(i);
ValueKind kind = phiNode.getKind();
if (!isStorableKind(kind))
continue;
RegisterClassKind classKind = rckGeneral; // FIX: get class kind from phi node kind.
Uint32 nInputs = phiNode.nInputs();
PhiNodeInstruction& phiNodeInstruction = *new(pool) PhiNodeInstruction(&phiNode, pool, nInputs);
emitter.defineProducer(phiNode, phiNodeInstruction, 0, classKind, drLow);
for (Uint32 whichInput = 0; whichInput < nInputs; whichInput++)
emitter.useProducer(phiNode.nthInputVariable(whichInput), phiNodeInstruction, whichInput, classKind, drLow);
node.addPhiNodeInstruction(phiNodeInstruction);
if (isDoublewordKind(kind)) {
PhiNodeInstruction& phiNodeInstruction = *new(pool) PhiNodeInstruction(&phiNode, pool, nInputs);
emitter.defineProducer(phiNode, phiNodeInstruction, 0, classKind, drHigh);
for (Uint32 whichInput = 0; whichInput < nInputs; whichInput++)
emitter.useProducer(phiNode.nthInputVariable(whichInput), phiNodeInstruction, whichInput, classKind, drHigh);
node.addPhiNodeInstruction(phiNodeInstruction);
}
}
}
}
}
#ifdef DEBUG_LOG
void RegisterAllocatorTools::printSpillCosts(RegisterAllocator& registerAllocator)
{
LogModuleObject log = UT_LOG_MODULE(RegAlloc);
Uint32 rangeCount = registerAllocator.rangeCount;
SpillCost* cost = registerAllocator.spillCost;
UT_OBJECTLOG(log, PR_LOG_ALWAYS, ("Spill costs:\n"));
for (Uint32 i = 1; i < rangeCount; i++) {
UT_OBJECTLOG(log, PR_LOG_ALWAYS, ("\trange %d : ", i));
if (cost[i].infinite)
UT_OBJECTLOG(log, PR_LOG_ALWAYS, ("infinite\n"));
else
UT_OBJECTLOG(log, PR_LOG_ALWAYS, ("%f\n", cost[i].cost));
}
}
void RegisterAllocatorTools::printSplitCosts(RegisterAllocator& registerAllocator)
{
LogModuleObject log = UT_LOG_MODULE(RegAlloc);
Uint32 rangeCount = registerAllocator.rangeCount;
SplitCost* cost = registerAllocator.splitCost;
UT_OBJECTLOG(log, PR_LOG_ALWAYS, ("Split costs:\n"));
for (Uint32 i = 1; i < rangeCount; i++) {
UT_OBJECTLOG(log, PR_LOG_ALWAYS, ("\trange %d : loads = %f stores = %f\n", i, cost[i].loads, cost[i].stores));
}
}
void RegisterAllocatorTools::printInstructions(RegisterAllocator& registerAllocator)
{
LogModuleObject log = UT_LOG_MODULE(RegAlloc);
ControlNode** nodes = registerAllocator.controlGraph.dfsList;
Uint32 nNodes = registerAllocator.controlGraph.nNodes;
for (Uint32 n = 0; n < nNodes; n++) {
UT_OBJECTLOG(log, PR_LOG_ALWAYS, ("N%d:\n", n));
InstructionList& phiNodes = nodes[n]->getPhiNodeInstructions();
InstructionList& instructions = nodes[n]->getInstructions();
if (!phiNodes.empty()) {
UT_OBJECTLOG(log, PR_LOG_ALWAYS, (" PhiNodes:\n", n));
for(InstructionList::iterator i = phiNodes.begin(); !phiNodes.done(i); i = phiNodes.advance(i)) {
phiNodes.get(i).printPretty(log);
UT_OBJECTLOG(log, PR_LOG_ALWAYS, ("\n"));
}
if (!instructions.empty())
UT_OBJECTLOG(log, PR_LOG_ALWAYS, (" Instructions:\n", n));
}
for(InstructionList::iterator i = instructions.begin(); !instructions.done(i); i = instructions.advance(i)) {
instructions.get(i).printPretty(log);
UT_OBJECTLOG(log, PR_LOG_ALWAYS, ("\n"));
}
}
}
#endif // DEBUG_LOG

View File

@@ -0,0 +1,117 @@
// -*- mode:C++; tab-width:4; truncate-lines:t -*-
//
// CONFIDENTIAL AND PROPRIETARY SOURCE CODE OF
// NETSCAPE COMMUNICATIONS CORPORATION
// Copyright © 1996, 1997 Netscape Communications Corporation. All Rights
// Reserved. Use of this Source Code is subject to the terms of the
// applicable license agreement from Netscape Communications Corporation.
// The copyright notice(s) in this Source Code does not indicate actual or
// intended publication of this Source Code.
//
// $Id: RegisterAllocatorTools.h,v 1.1.2.1 1999-03-02 16:12:05 fur%netscape.com Exp $
//
#ifndef _REGISTER_ALLOCATOR_TOOLS_H_
#define _REGISTER_ALLOCATOR_TOOLS_H_
#include "LogModule.h"
#include "RegisterTypes.h"
#include <string.h>
class RegisterAllocator;
class ControlGraph;
class InstructionEmitter;
class VirtualRegisterManager;
struct RegisterAllocatorTools
{
//
//
static void insertPhiNodeInstructions(ControlGraph& controlGraph, InstructionEmitter& emitter);
//
//
static void updateInstructionGraph(RegisterAllocator& registerAllocator);
//
//
static void removeUnnecessaryCopies(RegisterAllocator& registerAllocator);
#ifdef DEBUG
//
//
static void testTheInstructionGraph(ControlGraph& controlGraph, VirtualRegisterManager& vrManager);
#endif // DEBUG
#ifdef DEBUG_LOG
//
//
static void printInstructions(RegisterAllocator& registerAllocator);
//
//
static void printSpillCosts(RegisterAllocator& registerAllocator);
//
//
static void printSplitCosts(RegisterAllocator& registerAllocator);
#endif // DEBUG_LOG
};
//
// FIX: this should go in a class (LookupTable ?)
//
inline RegisterName findRoot(RegisterName name, RegisterName* table)
{
RegisterName* stack = table;
RegisterName* stackPtr = stack;
RegisterName newName;
while((newName = table[name]) != name) {
*--stackPtr = name;
name = newName;
}
while (stackPtr != stack)
table[*stackPtr++] = name;
return name;
}
inline void init(RegisterName* table, Uint32 nameCount)
{
for (RegisterName r = RegisterName(0); r < nameCount; r = RegisterName(r + 1))
table[r] = r;
}
inline Uint32 compress(RegisterName* name2range, RegisterName* table, Uint32 nameCount, Uint32 tableSize)
{
RegisterName* liveRange = new RegisterName[tableSize];
memset(liveRange, '\0', tableSize * sizeof(RegisterName));
// Update the lookup table.
for (RegisterName r = RegisterName(1); r < tableSize; r = RegisterName(r + 1))
findRoot(r, table);
// Count the liveranges.
Uint32 liveRangeCount = 1;
for (RegisterName s = RegisterName(1); s < tableSize; s = RegisterName(s + 1))
if (table[s] == s)
liveRange[s] = RegisterName(liveRangeCount++);
for (RegisterName t = RegisterName(1); t < nameCount; t = RegisterName(t + 1))
name2range[t] = liveRange[table[name2range[t]]];
return liveRangeCount;
}
inline double doLog10(Uint32 power)
{
double log = 1.0;
while (power--)
log *= 10.0;
return log;
}
#endif // _REGISTER_ALLOCATOR_TOOLS_H_

View File

@@ -1,4 +1,4 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
/* -*- 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.0 (the "NPL"); you may not use this file except in
@@ -12,24 +12,27 @@
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
// API class
#ifndef _REGISTER_ASSIGNER_H_
#define _REGISTER_ASSIGNER_H_
package org.mozilla.javascript;
#include "Fundamentals.h"
#include "VirtualRegister.h"
/**
* Thrown if call is attempted on an object that is not a function.
*/
public class NotAFunctionException extends Exception {
class FastBitMatrix;
public NotAFunctionException() {
}
class RegisterAssigner
{
protected:
VirtualRegisterManager& vRegManager;
public:
RegisterAssigner(VirtualRegisterManager& vrMan) : vRegManager(vrMan) {}
virtual bool assignRegisters(FastBitMatrix& interferenceMatrix) = 0;
};
public NotAFunctionException(String detail) {
super(detail);
}
}
#endif /* _REGISTER_ASSIGNER_H_ */

View File

@@ -1,4 +1,4 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
/* -*- 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.0 (the "NPL"); you may not use this file except in
@@ -12,17 +12,14 @@
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
package org.mozilla.javascript;
#ifndef _REGISTER_CLASS_H_
#define _REGISTER_CLASS_H_
/**
* Objects that can wrap other values for reflection in the JS environment.
*/
#include "Fundamentals.h"
#include "RegisterTypes.h"
public interface Wrapper {
public Object unwrap();
}
#endif // _REGISTER_CLASS_H_

View File

@@ -1,4 +1,4 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
/* -*- 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.0 (the "NPL"); you may not use this file except in
@@ -12,24 +12,26 @@
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
// API class
#ifndef _REGISTER_PRESSURE_H_
#define _REGISTER_PRESSURE_H_
package org.mozilla.javascript;
#include "BitSet.h"
#include "HashSet.h"
/**
* Thrown if call is attempted on an object that is not a function.
*/
public class NotAFunctionException extends Exception {
struct LowRegisterPressure
{
typedef BitSet Set;
static const bool setIsOrdered = true;
};
public NotAFunctionException() {
}
struct HighRegisterPressure
{
typedef HashSet Set;
static const bool setIsOrdered = false;
};
public NotAFunctionException(String detail) {
super(detail);
}
}
#endif // _REGISTER_PRESSURE_H_

View File

@@ -0,0 +1,104 @@
/* -*- 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#ifndef _REGISTER_TYPES_H_
#define _REGISTER_TYPES_H_
#include "Fundamentals.h"
//------------------------------------------------------------------------------
// RegisterName -
//
enum RegisterName {
rnInvalid = 0,
};
//------------------------------------------------------------------------------
// RegisterClassKind -
//
enum RegisterClassKind {
rckInvalid = 0,
rckGeneral,
rckStackSlot,
nRegisterClassKind
};
//------------------------------------------------------------------------------
// RegisterID -
//
enum RegisterID {
invalidID = 0
};
//------------------------------------------------------------------------------
// RegisterKind -
//
enum RegisterKind {
rkCallerSave = 0,
rkCalleeSave,
};
struct NameLinkedList {
RegisterName name;
NameLinkedList* next;
};
#ifdef DEBUG
const registerNameMask = 0x03ffffff;
const coloredRegisterMask = 0x04000000;
const machineRegisterMask = 0x08000000;
const registerClassMask = 0xf0000000;
const registerNameShift = 0;
const coloredRegisterShift = 26;
const machineRegisterShift = 27;
const registerClassShift = 28;
#else // DEBUG
const registerNameMask = 0x0fffffff;
const registerClassMask = 0xf0000000;
const registerNameShift = 0;
const registerClassShift = 28;
#endif // DEBUG
inline RegisterClassKind getClass(RegisterID registerID) {return RegisterClassKind((registerID & registerClassMask) >> registerClassShift);}
inline RegisterName getName(RegisterID registerID) {return RegisterName((registerID & registerNameMask) >> registerNameShift);}
inline void setClass(RegisterID& registerID, RegisterClassKind classKind) {registerID = RegisterID((registerID & ~registerClassMask) | ((classKind << registerClassShift) & registerClassMask));}
inline void setName(RegisterID& registerID, RegisterName name) {assert((name & ~registerNameMask) == 0); registerID = RegisterID((registerID & ~registerNameMask) | ((name << registerNameShift) & registerNameMask));}
inline RegisterID buildRegisterID(RegisterName name, RegisterClassKind classKind) {return RegisterID(((classKind << registerClassShift) & registerClassMask) | ((name << registerNameShift) & registerNameMask));}
#ifdef DEBUG
inline bool isMachineRegister(RegisterID rid) {return (rid & machineRegisterMask) != 0;}
inline void setMachineRegister(RegisterID& rid) {rid = RegisterID(rid | machineRegisterMask);}
inline bool isColoredRegister(RegisterID rid) {return (rid & coloredRegisterMask) != 0;}
inline void setColoredRegister(RegisterID& rid) {rid = RegisterID(rid | coloredRegisterMask);}
#endif // DEBUG
#endif // _REGISTER_TYPES_H_

View File

@@ -1,4 +1,4 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
/* -*- 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.0 (the "NPL"); you may not use this file except in
@@ -12,22 +12,21 @@
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
// API class
#include "Fundamentals.h"
#include "SSATools.h"
#include "ControlGraph.h"
#include "VirtualRegister.h"
#include "Liveness.h"
package org.mozilla.javascript;
/**
* Thrown if errors are detected while attempting to define a property of
* a host object from a Java class or method, or if a property is not found.
*/
public class PropertyException extends Exception {
public PropertyException(String detail) {
super(detail);
}
void replacePhiNodes(ControlGraph& controlGraph, VirtualRegisterManager& vrManager)
{
if (!controlGraph.hasBackEdges)
return;
Liveness liveness(controlGraph.pool);
liveness.buildLivenessAnalysis(controlGraph, vrManager);
}

View File

@@ -1,5 +1,4 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
/* -*- 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.0 (the "NPL"); you may not use this file except in
@@ -13,20 +12,18 @@
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
// API class
package org.mozilla.javascript;
#ifndef _SSA_TOOLS_H_
#define _SSA_TOOLS_H_
/**
* Thrown if errors are detected while attempting to define a host object
* from a Java class.
*/
public class ClassDefinitionException extends Exception {
#include "Fundamentals.h"
public ClassDefinitionException(String detail) {
super(detail);
}
}
class ControlGraph;
class VirtualRegisterManager;
extern void replacePhiNodes(ControlGraph& controlGraph, VirtualRegisterManager& vrManager);
#endif // _SSA_TOOLS_H_

View File

@@ -1,4 +1,4 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
/* -*- 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.0 (the "NPL"); you may not use this file except in
@@ -12,24 +12,26 @@
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
package org.mozilla.javascript.regexp;
#include "Fundamentals.h"
#include "SparseSet.h"
#include "BitSet.h"
#include "Pool.h"
class SubString {
#ifdef DEBUG_LOG
// Print the set.
//
void SparseSet::printPretty(LogModuleObject log)
{
Pool pool;
BitSet set(pool, universeSize);
public String toString() {
return charArray == null
? ""
: new String(charArray, index, length);
}
for (Uint32 i = 0; i < count; i++)
set.set(node[i].element);
static final SubString emptySubString = new SubString();
char[] charArray;
int index;
int length;
set.printPretty(log);
}
#endif // DEBUG_LOG

View File

@@ -0,0 +1,168 @@
// -*- mode:C++; tab-width:4; truncate-lines:t -*-
//
// CONFIDENTIAL AND PROPRIETARY SOURCE CODE OF
// NETSCAPE COMMUNICATIONS CORPORATION
// Copyright © 1996, 1997 Netscape Communications Corporation. All Rights
// Reserved. Use of this Source Code is subject to the terms of the
// applicable license agreement from Netscape Communications Corporation.
// The copyright notice(s) in this Source Code does not indicate actual or
// intended publication of this Source Code.
//
// $Id: SparseSet.h,v 1.1.2.1 1999-03-02 16:12:07 fur%netscape.com Exp $
//
#ifndef _SPARSE_SET_H_
#define _SPARSE_SET_H_
#include "Fundamentals.h"
#include "Pool.h"
#include "LogModule.h"
#include "BitSet.h"
class SparseSet
{
private:
struct Node {
Uint32 element;
Uint32 stackIndex;
};
Node* node;
Uint32 count;
Uint32 universeSize;
private:
// No copy constructor.
SparseSet(const SparseSet&);
// Check if the given set's universe is of the same size than this universe.
void checkUniverseCompatibility(const SparseSet& set) const {assert(set.universeSize == universeSize);}
// Check if pos is valid for this set's universe.
void checkMember(Int32 pos) const {assert(pos >=0 && Uint32(pos) < universeSize);}
public:
SparseSet(Pool& pool, Uint32 universeSize) : universeSize(universeSize) {node = new(pool) Node[universeSize]; clear();}
// Clear the sparse set.
void clear() {count = 0;}
// Clear the element at index.
inline void clear(Uint32 index);
// Set the element at index.
inline void set(Uint32 index);
// Return true if the element at index is set.
inline bool test(Uint32 index) const;
// Union with the given sparse set.
inline void or(const SparseSet& set);
// Intersection with the given sparse set.
inline void and(const SparseSet& set);
// Difference with the given sparse set.
inline void difference(const SparseSet& set);
// Copy set.
inline SparseSet& operator = (const SparseSet& set);
inline SparseSet& operator = (const BitSet& set);
// Return true if the sparse sets are identical.
friend bool operator == (const SparseSet& set1, const SparseSet& set2);
// Return true if the sparse sets are different.
friend bool operator != (const SparseSet& set1, const SparseSet& set2);
// Logical operators.
SparseSet& operator |= (const SparseSet& set) {or(set); return *this;}
SparseSet& operator &= (const SparseSet& set) {and(set); return *this;}
SparseSet& operator -= (const SparseSet& set) {difference(set); return *this;}
// Iterator to conform with the set API.
typedef Int32 iterator;
// Return the iterator for the first element of this set.
iterator begin() const {return count - 1;}
// Return the next iterator.
iterator advance(iterator pos) const {return --pos;}
// Return true if the iterator is at the end of the set.
bool done(iterator pos) const {return pos < 0;}
// Return the element for the given iterator;
Uint32 get(iterator pos) const {return node[pos].element;}
// Return one element of this set.
Uint32 getOne() const {assert(count > 0); return node[0].element;}
// Return the size of this set.
Uint32 getSize() const {return count;}
#ifdef DEBUG_LOG
// Print the set.
void printPretty(LogModuleObject log);
#endif // DEBUG_LOG
};
inline void SparseSet::clear(Uint32 element)
{
checkMember(element);
Uint32 count = this->count;
Node* node = this->node;
Uint32 stackIndex = node[element].stackIndex;
if ((stackIndex < count) && (node[stackIndex].element == element)) {
Uint32 stackTop = node[count - 1].element;
node[stackIndex].element = stackTop;
node[stackTop].stackIndex = stackIndex;
this->count = count - 1;
}
}
inline void SparseSet::set(Uint32 element)
{
checkMember(element);
Uint32 count = this->count;
Node* node = this->node;
Uint32 stackIndex = node[element].stackIndex;
if ((stackIndex >= count) || (node[stackIndex].element != element)) {
node[count].element = element;
node[element].stackIndex = count;
this->count = count + 1;
}
}
inline bool SparseSet::test(Uint32 element) const
{
checkMember(element);
Node* node = this->node;
Uint32 stackIndex = node[element].stackIndex;
return ((stackIndex < count) && (node[stackIndex].element == element));
}
inline SparseSet& SparseSet::operator = (const SparseSet& set)
{
checkUniverseCompatibility(set);
Uint32 sourceCount = set.getSize();
Node* node = this->node;
memcpy(node, set.node, sourceCount * sizeof(Node));
for (Uint32 i = 0; i < sourceCount; i++) {
Uint32 element = node[i].element;
node[element].stackIndex = i;
}
count = sourceCount;
return *this;
}
inline SparseSet& SparseSet::operator = (const BitSet& set)
{
// FIX: there's room for optimization here.
assert(universeSize == set.getSize());
clear();
for (Int32 i = set.firstOne(); i != -1; i = set.nextOne(i))
this->set(i);
return *this;
}
#endif // _SPARSE_SET_H_

View File

@@ -0,0 +1,270 @@
/* -*- 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#ifndef NEW_LAURENTM_CODE
#define INCLUDE_EMITTER
#include "CpuInfo.h"
#include "Fundamentals.h"
#include "ControlNodes.h"
#include "Instruction.h"
#include "InstructionEmitter.h"
#include "Spilling.h"
void Spilling::
insertSpillCode(ControlNode** dfsList, Uint32 nNodes)
{
PRUint32 nVirtualRegisters = vRegManager.count();
FastBitSet currentLive(vRegManager.pool, nVirtualRegisters);
FastBitSet usedInThisInstruction(vRegManager.pool, nVirtualRegisters);
RegisterFifo grNeedLoad(nVirtualRegisters);
RegisterFifo fpNeedLoad(nVirtualRegisters);
for (PRInt32 n = nNodes - 1; n >= 0; n--)
{
PR_ASSERT(grNeedLoad.empty() & fpNeedLoad.empty());
ControlNode& node = *dfsList[n];
currentLive = node.liveAtEnd;
PRUint32 nGeneralAlive = 0;
PRUint32 nFloatingPointAlive = 0;
// Get the number of registers alive at the end of this node.
for (PRInt32 j = currentLive.firstOne(); j != -1; j = currentLive.nextOne(j))
{
VirtualRegister& vReg = vRegManager.getVirtualRegister(j);
if (vReg.spillInfo.willSpill)
{
currentLive.clear(j);
}
else
{
switch (vReg.getClass())
{
case vrcInteger:
nGeneralAlive++;
break;
case vrcFloatingPoint:
case vrcFixedPoint:
nFloatingPointAlive++;
break;
default:
break;
}
}
}
// if(node.dfsNum == 8) printf("\n________Begin Node %d________\n", node.dfsNum);
InstructionList& instructions = node.getInstructions();
for (InstructionList::iterator i = instructions.end(); !instructions.done(i); i = instructions.retreat(i))
{
Instruction& instruction = instructions.get(i);
InstructionUse* useBegin = instruction.getInstructionUseBegin();
InstructionUse* useEnd = instruction.getInstructionUseEnd();
InstructionUse* usePtr;
InstructionDefine* defBegin = instruction.getInstructionDefineBegin();
InstructionDefine* defEnd = instruction.getInstructionDefineEnd();
InstructionDefine* defPtr;
// if(node.dfsNum == 8) { printf("\n");
// instruction.printPretty(stdout);
// printf("\n"); }
// Handle definitions
for (defPtr = defBegin; defPtr < defEnd; defPtr++)
if (defPtr->isVirtualRegister())
{
VirtualRegister& vReg = defPtr->getVirtualRegister();
currentLive.clear(vReg.getRegisterIndex());
switch (vReg.getClass())
{
case vrcInteger:
nGeneralAlive--;
break;
case vrcFloatingPoint:
case vrcFixedPoint:
nFloatingPointAlive--;
break;
default:
break;
}
}
// Check for deaths
for (usePtr = useBegin; usePtr < useEnd; usePtr++)
if (usePtr->isVirtualRegister())
{
VirtualRegister& vReg = usePtr->getVirtualRegister();
if (!currentLive.test(vReg.getRegisterIndex()))
// This is the last use of this register.
{
currentLive.set(vReg.getRegisterIndex());
switch (vReg.getClass())
{
case vrcInteger:
nGeneralAlive++;
while (/*(nGeneralAlive > NUMBER_OF_GREGISTERS) &&*/ !grNeedLoad.empty())
{
PRUint32 toLoad = grNeedLoad.get();
currentLive.clear(toLoad);
nGeneralAlive--;
VirtualRegister& nReg = vRegManager.getVirtualRegister(toLoad);
Instruction& lastUsingInstruction = *nReg.spillInfo.lastUsingInstruction;
emitter.emitLoadAfter(*lastUsingInstruction.getPrimitive(), lastUsingInstruction.getLinks().prev,
nReg.getAlias(), *nReg.equivalentRegister[vrcStackSlot]);
nReg.releaseSelf();
}
break;
case vrcFloatingPoint:
case vrcFixedPoint:
nFloatingPointAlive++;
while (/*(nFloatingPointAlive > NUMBER_OF_FPREGISTERS) &&*/ !fpNeedLoad.empty())
{
PRUint32 toLoad = fpNeedLoad.get();
currentLive.clear(toLoad);
nFloatingPointAlive--;
VirtualRegister& nReg = vRegManager.getVirtualRegister(toLoad);
Instruction& lastUsingInstruction = *nReg.spillInfo.lastUsingInstruction;
emitter.emitLoadAfter(*lastUsingInstruction.getPrimitive(), lastUsingInstruction.getLinks().prev,
nReg.getAlias(), *nReg.equivalentRegister[vrcStackSlot]);
nReg.releaseSelf();
}
break;
default:
break;
}
}
}
// Handle uses
for (usePtr = useBegin; usePtr < useEnd; usePtr++)
if (usePtr->isVirtualRegister())
{
VirtualRegister& vReg = usePtr->getVirtualRegister();
PRUint32 registerIndex = vReg.getRegisterIndex();
if (vReg.spillInfo.willSpill) {
#if defined(GENERATE_FOR_X86)
if (!instruction.switchUseToSpill((usePtr - useBegin), *vReg.equivalentRegister[vrcStackSlot]))
#endif
{
switch (vReg.getClass())
{
case vrcInteger:
if (!grNeedLoad.test(registerIndex))
{
grNeedLoad.put(registerIndex);
VirtualRegister& alias = vRegManager.newVirtualRegister(vrcInteger);
if (vReg.isPreColored())
alias.preColorRegister(vReg.getPreColor());
/* if (vReg.hasSpecialInterference) {
alias.specialInterference.sizeTo(NUMBER_OF_REGISTERS);
alias.specialInterference = vReg.specialInterference;
alias.hasSpecialInterference = true;
} */
vReg.setAlias(alias);
vReg.retainSelf();
}
break;
case vrcFloatingPoint:
case vrcFixedPoint:
if (!fpNeedLoad.test(registerIndex))
{
fpNeedLoad.put(registerIndex);
VirtualRegister& alias = vRegManager.newVirtualRegister(vReg.getClass());
if (vReg.isPreColored())
alias.preColorRegister(vReg.getPreColor());
/*if (vReg.hasSpecialInterference) {
alias.specialInterference.sizeTo(NUMBER_OF_REGISTERS);
alias.specialInterference = vReg.specialInterference;
alias.hasSpecialInterference = true;
} */
vReg.setAlias(alias);
vReg.retainSelf();
}
break;
default:
break;
}
usePtr->getVirtualRegisterPtr().initialize(vReg.getAlias());
usedInThisInstruction.set(registerIndex);
vReg.spillInfo.lastUsingInstruction = &instruction;
}
currentLive.clear(registerIndex);
} else { // will not spill
currentLive.set(registerIndex);
}
}
// Handle definitions
for (defPtr = defBegin; defPtr < defEnd; defPtr++)
if (defPtr->isVirtualRegister())
{
VirtualRegister& vReg = defPtr->getVirtualRegister();
if (vReg.spillInfo.willSpill)
#if defined(GENERATE_FOR_X86)
if (!instruction.switchDefineToSpill((defPtr - defBegin), *vReg.equivalentRegister[vrcStackSlot]))
#endif
{
if (usedInThisInstruction.test(vReg.getRegisterIndex()))
// this virtualRegister was used in this instruction and is also defined. We need to move
// this virtual register to its alias first and then save it to memory.
{
emitter.emitStoreAfter(*instruction.getPrimitive(), &instruction.getLinks(),
vReg.getAlias(), *vReg.equivalentRegister[vrcStackSlot]);
defPtr->getVirtualRegisterPtr().initialize(vReg.getAlias());
}
else
{
emitter.emitStoreAfter(*instruction.getPrimitive(), &instruction.getLinks(),
vReg, *vReg.equivalentRegister[vrcStackSlot]);
}
}
}
}
while (!grNeedLoad.empty())
{
PRUint32 nl = grNeedLoad.get();
VirtualRegister& nlReg = vRegManager.getVirtualRegister(nl);
Instruction& lastUse = *nlReg.spillInfo.lastUsingInstruction;
emitter.emitLoadAfter(*lastUse.getPrimitive(), lastUse.getLinks().prev,
nlReg.getAlias(), *nlReg.equivalentRegister[vrcStackSlot]);
nlReg.releaseSelf();
}
while (!fpNeedLoad.empty())
{
PRUint32 nl = fpNeedLoad.get();
VirtualRegister& nlReg = vRegManager.getVirtualRegister(nl);
Instruction& lastUse = *nlReg.spillInfo.lastUsingInstruction;
emitter.emitLoadAfter(*lastUse.getPrimitive(), lastUse.getLinks().prev,
nlReg.getAlias(), *nlReg.equivalentRegister[vrcStackSlot]);
nlReg.releaseSelf();
}
// if(node.dfsNum == 8) printf("\n________End Node %d________\n", node.dfsNum);
}
}
#endif

View File

@@ -0,0 +1,269 @@
/* -*- 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#ifndef _SPILLING_H_
#define _SPILLING_H_
#include "Fundamentals.h"
#include <string.h>
#include "RegisterAllocator.h"
#include "RegisterAllocatorTools.h"
#include "ControlGraph.h"
#include "ControlNodes.h"
#include "Instruction.h"
#include "SparseSet.h"
template <class RegisterPressure>
class Spilling
{
private:
static void insertStoreAfter(Instruction& instruction, RegisterName name);
static void insertLoadBefore(Instruction& instruction, RegisterName name);
public:
static void calculateSpillCosts(RegisterAllocator& registerAllocator);
static void insertSpillCode(RegisterAllocator& registerAllocator);
};
struct SpillCost
{
double loads;
double stores;
double copies;
double cost;
bool infinite;
};
template <class RegisterPressure>
void Spilling<RegisterPressure>::insertSpillCode(RegisterAllocator& registerAllocator)
{
Uint32 rangeCount = registerAllocator.rangeCount;
RegisterName* name2range = registerAllocator.name2range;
Pool& pool = registerAllocator.pool;
SparseSet currentLive(pool, rangeCount);
SparseSet needLoad(pool, rangeCount);
SparseSet mustSpill(pool, rangeCount);
SparseSet& willSpill = *registerAllocator.willSpill;
ControlGraph& controlGraph = registerAllocator.controlGraph;
RegisterPressure::Set* liveOut = registerAllocator.liveness.liveOut;
ControlNode** nodes = controlGraph.dfsList;
Uint32 nNodes = controlGraph.nNodes;
for (Uint32 n = 0; n < nNodes; n++) {
needLoad.clear();
currentLive = liveOut[n];
mustSpill = currentLive;
InstructionList& instructions = nodes[n]->getInstructions();
for (InstructionList::iterator i = instructions.end(); !instructions.done(i);) {
Instruction& instruction = instructions.get(i);
i = instructions.retreat(i);
InstructionUse* useBegin = instruction.getInstructionUseBegin();
InstructionUse* useEnd = instruction.getInstructionUseEnd();
InstructionUse* usePtr;
InstructionDefine* defineBegin = instruction.getInstructionDefineBegin();
InstructionDefine* defineEnd = instruction.getInstructionDefineEnd();
InstructionDefine* definePtr;
bool foundLiveDefine = false;
for (definePtr = defineBegin; definePtr < defineEnd; definePtr++)
if (definePtr->isRegister()) {
if (currentLive.test(name2range[definePtr->getRegisterName()])) {
foundLiveDefine = true;
break;
}
} else {
foundLiveDefine = true;
break;
}
if (defineBegin != defineEnd && !foundLiveDefine) {
fprintf(stderr, "!!! Removed instruction because it was only defining unused registers !!!\n");
instruction.remove();
}
for (definePtr = defineBegin; definePtr < defineEnd; definePtr++)
if (definePtr->isRegister()) {
RegisterName range = name2range[definePtr->getRegisterName()];
#ifdef DEBUG
if (needLoad.test(range))
if (!mustSpill.test(range) && registerAllocator.spillCost[range].infinite && willSpill.test(range)) {
fprintf(stderr, "Tried to spill a register with infinite spill cost\n");
abort();
}
#endif // DEBUG
if (willSpill.test(range))
insertStoreAfter(instruction, range);
needLoad.clear(range);
}
if (instruction.getFlags() & ifCopy)
for (usePtr = useBegin; usePtr < useEnd; usePtr++)
if (usePtr->isRegister()) {
RegisterName range = name2range[usePtr->getRegisterName()];
if (!currentLive.test(range))
for (SparseSet::iterator r = needLoad.begin(); !needLoad.done(r); r = needLoad.advance(r)) {
RegisterName load = RegisterName(needLoad.get(r));
if (willSpill.test(load))
insertLoadBefore(instruction, load);
mustSpill.set(load);
}
needLoad.clear();
}
for (definePtr = defineBegin; definePtr < defineEnd; definePtr++)
if (definePtr->isRegister())
currentLive.clear(name2range[definePtr->getRegisterName()]);
for (usePtr = useBegin; usePtr < useEnd; usePtr++)
if (usePtr->isRegister()) {
RegisterName range = name2range[usePtr->getRegisterName()];
currentLive.set(range);
needLoad.set(range);
}
}
for (SparseSet::iterator l = needLoad.begin(); !needLoad.done(l); l = needLoad.advance(l)) {
RegisterName load = RegisterName(needLoad.get(l));
if (willSpill.test(load))
insertLoadBefore(instructions.first(), load);
}
}
}
template <class RegisterPressure>
void Spilling<RegisterPressure>::insertLoadBefore(Instruction& /*instruction*/, RegisterName name)
{
fprintf(stdout, "will insert load for range %d\n", name);
}
template <class RegisterPressure>
void Spilling<RegisterPressure>::insertStoreAfter(Instruction& /*instruction*/, RegisterName name)
{
fprintf(stdout, "will insert store for range %d\n", name);
}
template <class RegisterPressure>
void Spilling<RegisterPressure>::calculateSpillCosts(RegisterAllocator& registerAllocator)
{
Uint32 rangeCount = registerAllocator.rangeCount;
RegisterName* name2range = registerAllocator.name2range;
Pool& pool = registerAllocator.pool;
SparseSet live(pool, rangeCount);
SparseSet needLoad(pool, rangeCount);
SparseSet mustSpill(pool, rangeCount);
SparseSet alreadyStored(pool, rangeCount); // FIX: should get this from previous spilling.
SpillCost* cost = new SpillCost[rangeCount];
memset(cost, '\0', rangeCount * sizeof(SpillCost));
ControlGraph& controlGraph = registerAllocator.controlGraph;
RegisterPressure::Set* liveOut = registerAllocator.liveness.liveOut;
ControlNode** nodes = controlGraph.dfsList;
Uint32 nNodes = controlGraph.nNodes;
for (Uint32 n = 0; n < nNodes; n++) {
ControlNode& node = *nodes[n];
double weight = doLog10(node.loopDepth);
needLoad.clear();
live = liveOut[n];
mustSpill = live;
InstructionList& instructions = nodes[n]->getInstructions();
for (InstructionList::iterator i = instructions.end(); !instructions.done(i); i = instructions.retreat(i)) {
Instruction& instruction = instructions.get(i);
InstructionUse* useBegin = instruction.getInstructionUseBegin();
InstructionUse* useEnd = instruction.getInstructionUseEnd();
InstructionUse* usePtr;
InstructionDefine* defineBegin = instruction.getInstructionDefineBegin();
InstructionDefine* defineEnd = instruction.getInstructionDefineEnd();
InstructionDefine* definePtr;
for (definePtr = defineBegin; definePtr < defineEnd; definePtr++)
if (definePtr->isRegister()) {
RegisterName range = name2range[definePtr->getRegisterName()];
if (needLoad.test(range))
if (!mustSpill.test(range))
cost[range].infinite = true;
if ((false /* !rematerializable(range) */ || !needLoad.test(range)) && !alreadyStored.test(range))
cost[range].stores += weight;
needLoad.clear(range);
}
if (instruction.getFlags() & ifCopy)
for (usePtr = useBegin; usePtr < useEnd; usePtr++)
if (usePtr->isRegister())
if (!live.test(name2range[usePtr->getRegisterName()])) {
for (SparseSet::iterator l = needLoad.begin(); !needLoad.done(l); l = needLoad.advance(l)) {
Uint32 range = needLoad.get(l);
cost[range].loads += weight;
mustSpill.set(range);
}
needLoad.clear();
}
for (definePtr = defineBegin; definePtr < defineEnd; definePtr++)
if (definePtr->isRegister())
live.clear(name2range[definePtr->getRegisterName()]);
for (usePtr = useBegin; usePtr < useEnd; usePtr++)
if (usePtr->isRegister()) {
RegisterName range = name2range[usePtr->getRegisterName()];
live.set(range);
needLoad.set(range);
}
if (instruction.getFlags() & ifCopy) {
assert(useBegin != useEnd && useBegin[0].isRegister());
assert(defineBegin != defineEnd && defineBegin[0].isRegister());
RegisterName source = name2range[useBegin[0].getRegisterName()];
RegisterName destination = name2range[defineBegin[0].getRegisterName()];
cost[source].copies += weight;
cost[destination].copies += weight;
}
}
for (SparseSet::iterator s = needLoad.begin(); !needLoad.done(s); s = needLoad.advance(s))
cost[needLoad.get(s)].loads += weight;
}
for (Uint32 r = 0; r < rangeCount; r++) {
SpillCost& c = cost[r];
c.cost = 2 * (c.loads + c.stores) - c.copies;
}
registerAllocator.spillCost = cost;
}
#endif // _SPILLING_H_

View File

@@ -0,0 +1,239 @@
/* -*- 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#ifndef _SPLITS_H_
#define _SPLITS_H_
#include "Fundamentals.h"
#include <string.h>
#include "Pool.h"
#include "ControlGraph.h"
#include "ControlNodes.h"
#include "Instruction.h"
#include "RegisterAllocator.h"
#include "RegisterAllocatorTools.h"
UT_EXTERN_LOG_MODULE(RegAlloc);
template <class RegisterPressure>
struct Splits
{
static void calculateSplitCosts(RegisterAllocator& registerAllocator);
static bool findSplit(RegisterAllocator& registerAllocator, RegisterName* color, RegisterName range);
static void insertSplitCode(RegisterAllocator& registerAllocator);
};
struct SplitCost
{
double loads;
double stores;
};
template <class RegisterPressure>
void Splits<RegisterPressure>::insertSplitCode(RegisterAllocator& /*registerAllocator*/)
{
// FIX
}
template <class RegisterPressure>
bool Splits<RegisterPressure>::findSplit(RegisterAllocator& registerAllocator, RegisterName* color, RegisterName range)
{
Pool& pool = registerAllocator.pool;
NameLinkedList** neighborsWithColor = new(pool) NameLinkedList*[6]; // FIX
memset(neighborsWithColor, '\0', 6 * sizeof(NameLinkedList*));
InterferenceGraph<RegisterPressure>& iGraph = registerAllocator.iGraph;
for (InterferenceVector* vector = iGraph.getInterferenceVector(range); vector != NULL; vector = vector->next)
for (Int32 i = vector->count - 1; i >=0; --i) {
RegisterName neighbor = vector->neighbors[i];
RegisterName c = color[neighbor];
if (c < 6) { // FIX
NameLinkedList* node = new(pool) NameLinkedList();
node->name = neighbor;
node->next = neighborsWithColor[c];
neighborsWithColor[c] = node;
}
}
bool splitAroundName = true;
LiveRangeGraph<RegisterPressure>& lGraph = registerAllocator.lGraph;
RegisterName bestColor = RegisterName(6); // FIX
double bestCost = registerAllocator.spillCost[range].cost;
SplitCost* splitCost = registerAllocator.splitCost;
for (RegisterName i = RegisterName(0); i < 6; i = RegisterName(i + 1)) { // FIX
double splitAroundNameCost = 0.0;
bool canSplitAroundName = true;
SplitCost& sCost = splitCost[range];
double addedCost = 2.0 * (sCost.stores + sCost.loads);
for (NameLinkedList* node = neighborsWithColor[i]; node != NULL; node = node->next) {
RegisterName neighbor = node->name;
if (lGraph.haveEdge(neighbor, range)) {
canSplitAroundName = false;
break;
} else
splitAroundNameCost += addedCost;
}
if (canSplitAroundName && splitAroundNameCost < bestCost) {
bestCost = splitAroundNameCost;
bestColor = i;
splitAroundName = true;
}
double splitAroundColorCost = 0.0;
bool canSplitAroundColor = true;
for (NameLinkedList* node = neighborsWithColor[i]; node != NULL; node = node->next) {
RegisterName neighbor = node->name;
if (lGraph.haveEdge(range, neighbor)) {
canSplitAroundColor = false;
break;
} else {
SplitCost& sCost = splitCost[neighbor];
double addedCost = 2.0 * (sCost.stores + sCost.loads);
splitAroundColorCost += addedCost;
}
}
if (canSplitAroundColor && splitAroundColorCost < bestCost) {
bestCost = splitAroundColorCost;
bestColor = i;
splitAroundName = false;
}
}
if (bestColor < RegisterName(6)) {
color[range] = bestColor;
registerAllocator.splitFound = true;
NameLinkedList** splitAround = registerAllocator.splitAround;
if (splitAroundName)
for (NameLinkedList* node = neighborsWithColor[bestColor]; node != NULL; node = node->next) {
NameLinkedList* newNode = new(pool) NameLinkedList();
newNode->name = node->name;
newNode->next = splitAround[range];
splitAround[range] = newNode;
}
else
for (NameLinkedList* node = neighborsWithColor[bestColor]; node != NULL; node = node->next) {
NameLinkedList* newNode = new(pool) NameLinkedList();
RegisterName neighbor = node->name;
newNode->name = range;
newNode->next = splitAround[neighbor];
splitAround[neighbor] = newNode;
}
trespass("Found a split");
return true;
}
return false;
}
template <class RegisterPressure>
void Splits<RegisterPressure>::calculateSplitCosts(RegisterAllocator& registerAllocator)
{
Pool& pool = registerAllocator.pool;
Uint32 rangeCount = registerAllocator.rangeCount;
RegisterName* name2range = registerAllocator.name2range;
SplitCost* splitCost = new(pool) SplitCost[rangeCount];
memset(splitCost, '\0', rangeCount * sizeof(SplitCost));
SparseSet live(pool, rangeCount);
RegisterPressure::Set* liveIn = registerAllocator.liveness.liveIn;
RegisterPressure::Set* liveOut = registerAllocator.liveness.liveOut;
ControlGraph& controlGraph = registerAllocator.controlGraph;
ControlNode** nodes = controlGraph.dfsList;
Uint32 nNodes = controlGraph.nNodes;
for (Uint32 n = 0; n < nNodes; n++) {
ControlNode& node = *nodes[n];
double weight = doLog10(node.loopDepth);
live = liveOut[n];
ControlEdge* successorsEnd = node.getSuccessorsEnd();
for (ControlEdge* successorsPtr = node.getSuccessorsBegin(); successorsPtr < successorsEnd; successorsPtr++) {
ControlNode& successor = successorsPtr->getTarget();
if (successor.getControlKind() != ckEnd) {
RegisterPressure::Set& successorLiveIn = liveIn[successor.dfsNum];
for (SparseSet::iterator i = live.begin(); !live.done(i); i = live.advance(i)) {
RegisterName name = RegisterName(live.get(i));
if (!successorLiveIn.test(name))
splitCost[name].loads += doLog10(successor.loopDepth);
}
}
}
InstructionList& instructions = node.getInstructions();
for (InstructionList::iterator i = instructions.end(); !instructions.done(i); i = instructions.retreat(i)) {
Instruction& instruction = instructions.get(i);
InstructionUse* useBegin = instruction.getInstructionUseBegin();
InstructionUse* useEnd = instruction.getInstructionUseEnd();
InstructionUse* usePtr;
InstructionDefine* defineBegin = instruction.getInstructionDefineBegin();
InstructionDefine* defineEnd = instruction.getInstructionDefineEnd();
InstructionDefine* definePtr;
for (definePtr = defineBegin; definePtr < defineEnd; definePtr++)
if (definePtr->isRegister())
splitCost[name2range[definePtr->getRegisterName()]].stores += weight;
for (usePtr = useBegin; usePtr < useEnd; usePtr++)
if (usePtr->isRegister()) {
RegisterName range = name2range[usePtr->getRegisterName()];
if (!live.test(range)) {
if (&instruction != &instructions.last())
splitCost[range].loads += weight;
else {
ControlEdge* successorsEnd = node.getSuccessorsEnd();
for (ControlEdge* successorsPtr = node.getSuccessorsBegin(); successorsPtr < successorsEnd; successorsPtr++)
splitCost[range].loads += doLog10(successorsPtr->getTarget().loopDepth);
}
}
}
for (definePtr = defineBegin; definePtr < defineEnd; definePtr++)
if (definePtr->isRegister())
live.clear(name2range[definePtr->getRegisterName()]);
for (usePtr = useBegin; usePtr < useEnd; usePtr++)
if (usePtr->isRegister())
live.set(name2range[usePtr->getRegisterName()]);
}
}
NameLinkedList** splitAround = new(pool) NameLinkedList*[rangeCount];
memset(splitAround, '\0', rangeCount * sizeof(NameLinkedList*));
registerAllocator.splitAround = splitAround;
registerAllocator.splitCost = splitCost;
registerAllocator.splitFound = false;
}
#endif // _SPLITS_H_

View File

@@ -0,0 +1,186 @@
/* -*- 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "Fundamentals.h"
#include "HashTable.h"
#include "Timer.h"
#include "Pool.h"
static Pool pool; // Pool for the Timer class.
static HashTable<TimerEntry*> timerEntries(pool); // Timers hashtable.
const nTimersInABlock = 128; // Number of timers in a block.
static PRTime *timers = new(pool) PRTime[nTimersInABlock]; // A block of timers.
static Uint8 nextTimer = 0; // nextAvailableTimer.
//
// Calibrate the call to PR_Now().
//
static PRTime calibrate()
{
PRTime t = PR_Now();
PRTime& a = *new(pool) PRTime();
// Call 10 times the PR_Now() function.
a = PR_Now(); a = PR_Now(); a = PR_Now(); a = PR_Now(); a = PR_Now(); a = PR_Now();
a = PR_Now(); a = PR_Now(); a = PR_Now(); a = PR_Now(); a = PR_Now(); a = PR_Now();
t = (PR_Now() - t + 9) / 10;
return t;
}
static PRTime adjust = calibrate();
//
// Return the named timer..
//
TimerEntry& Timer::getTimerEntry(const char* name)
{
if (!timerEntries.exists(name)) {
TimerEntry* newEntry = new(pool) TimerEntry();
newEntry->accumulator = 0;
newEntry->running = false;
timerEntries.add(name, newEntry);
}
return *timerEntries[name];
}
//
// Return a reference to a new timer.
//
PRTime& Timer::getNewTimer()
{
if (nextTimer >= nTimersInABlock) {
timers = new(pool) PRTime[nTimersInABlock];
nextTimer = 0;
}
return timers[nextTimer++];
}
static Uint32 timersAreFrozen = 0;
//
// Start the named timer.
//
void Timer::start(const char* name)
{
if (timersAreFrozen)
return;
freezeTimers();
TimerEntry& timer = getTimerEntry(name);
PR_ASSERT(!timer.running);
timer.accumulator = 0;
timer.running = true;
timer.done = false;
unfreezeTimers();
}
//
// Stop the named timer.
//
void Timer::stop(const char* name)
{
if (timersAreFrozen)
return;
freezeTimers();
TimerEntry& timer = getTimerEntry(name);
PR_ASSERT(timer.running);
timer.running = false;
timer.done = true;
unfreezeTimers();
}
//
// Freeze all the running timers.
//
void Timer::freezeTimers()
{
PRTime when = PR_Now() - adjust;
if (timersAreFrozen == 0) {
Vector<TimerEntry*> entries = timerEntries;
Uint32 count = entries.size();
for (Uint32 i = 0; i < count; i++) {
TimerEntry& entry = *entries[i];
if (entry.running) {
entry.accumulator += (when - *entry.startTime);
}
}
}
timersAreFrozen++;
}
//
// Unfreeze all the running timers.
//
void Timer::unfreezeTimers()
{
PR_ASSERT(timersAreFrozen != 0);
timersAreFrozen--;
if (timersAreFrozen == 0) {
Vector<TimerEntry *> entries = timerEntries;
Uint32 count = entries.size();
PRTime& newStart = getNewTimer();
for (Uint32 i = 0; i < count; i++) {
TimerEntry& entry = *entries[i];
if (entry.running) {
entry.startTime = &newStart;
}
}
newStart = PR_Now();
}
}
//
// Print the named timer in the file f.
//
void Timer::print(FILE* f, const char *name)
{
if (timersAreFrozen)
return;
freezeTimers();
TimerEntry& timer = getTimerEntry(name);
PR_ASSERT(timer.done);
PRTime elapsed = timer.accumulator;
if (elapsed >> 32) {
fprintf(f, "[timer %s out of range]\n", name);
} else {
fprintf(f, "[%dus in %s]\n", Uint32(elapsed), name);
}
fflush(f);
unfreezeTimers();
}

View File

@@ -0,0 +1,80 @@
/* -*- 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#ifndef _TIMER_H_
#define _TIMER_H_
#include "Fundamentals.h"
#include "HashTable.h"
#include "prtime.h"
//
// Naming convention:
// As the class Timer contains only static methods, the timer's name should start with the
// module name. Otherwise starting 2 timers with the same name will assert.
//
#ifndef NO_TIMER
struct TimerEntry
{
PRTime *startTime; // Current time when we start the timer.
PRTime accumulator; // Time spent in this timer.
bool running; // True if the timer is running.
bool done; // True if the timer was running and was stopped.
};
class Timer
{
private:
// Return the named timer.
static TimerEntry& getTimerEntry(const char* name);
// Return a reference to a new Timer.
static PRTime& getNewTimer();
public:
// Start the timer.
static void start(const char* name);
// Stop the timer.
static void stop(const char* name);
// Freeze all the running timers.
static void freezeTimers();
// Unfreeze all the running timers.
static void unfreezeTimers();
// Print the timer.
static void print(FILE* f, const char *name);
};
inline void startTimer(const char* name) {Timer::start(name);}
inline void stopTimer(const char* name) {Timer::stop(name); Timer::print(stdout, name);}
#define START_TIMER_SAFE Timer::freezeTimers();
#define END_TIMER_SAFE Timer::unfreezeTimers();
#define TIMER_SAFE(x) START_TIMER_SAFE x; END_TIMER_SAFE
#else /* NO_TIMER */
inline void startTimer(const char* /*name*/) {}
inline void stopTimer(const char* /*name*/) {}
#define START_TIMER_SAFE
#define END_TIMER_SAFE
#define TIMER_SAFE(x) x;
#endif /* NO_TIMER */
#endif /* _TIMER_H_ */

View File

@@ -0,0 +1,40 @@
/* -*- 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "Fundamentals.h"
#include "VirtualRegister.h"
#include "Instruction.h"
//------------------------------------------------------------------------------
// VirtualRegister -
#ifdef MANUAL_TEMPLATES
template class IndexedPool<VirtualRegister>;
#endif
// Set the defining instruction.
//
void VirtualRegister::setDefiningInstruction(Instruction& instruction)
{
if (definingInstruction != NULL) {
if ((instruction.getFlags() & ifCopy) && (definingInstruction->getFlags() & ifPhiNode))
return;
}
definingInstruction = &instruction;
}

View File

@@ -0,0 +1,116 @@
/* -*- 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#ifndef _VIRTUAL_REGISTER_H_
#define _VIRTUAL_REGISTER_H_
#include "Fundamentals.h"
#include "IndexedPool.h"
#include <string.h>
#include "RegisterTypes.h"
#include "RegisterClass.h"
//------------------------------------------------------------------------------
// VirtualRegister - 24b
class Instruction;
class VirtualRegister : public IndexedObject<VirtualRegister>
{
public:
Instruction* definingInstruction; // Instruction defining this VR.
// Initialize a VR of the given classKind.
VirtualRegister(RegisterClassKind /*classKind*/) : definingInstruction(NULL) {}
// Return the defining instruction for this VR.
Instruction* getDefiningInstruction() const {return definingInstruction;}
// Set the defining instruction.
void setDefiningInstruction(Instruction& insn);
};
// Return true if the VirtualRegisters are equals. The only way 2 VRs can be equal is if
// they have the same index. If they have the same index then they are at the same
// address in the indexed pool.
//
inline bool operator == (const VirtualRegister& regA, const VirtualRegister& regB) {return &regA == &regB;}
//------------------------------------------------------------------------------
// VirtualRegisterManager -
struct PreColoredRegister
{
RegisterID id;
RegisterName color;
};
class VirtualRegisterManager
{
private:
IndexedPool<VirtualRegister> registerPool;
PreColoredRegister machineRegister[6];
public:
VirtualRegisterManager()
{
for (Uint32 i = 0; i < 6; i++)
machineRegister[i].id = invalidID;
}
// Return the VirtualRegister at the given index.
VirtualRegister& getVirtualRegister(RegisterName name) const {return registerPool.get(name);}
// Return a new VirtualRegister.
RegisterID newVirtualRegister(RegisterClassKind classKind)
{
VirtualRegister& vReg = *new(registerPool) VirtualRegister(classKind);
RegisterID rid;
setName(rid, RegisterName(vReg.getIndex()));
setClass(rid, classKind);
return rid;
}
RegisterID newMachineRegister(RegisterName name, RegisterClassKind classKind)
{
RegisterID rid = machineRegister[name].id;
if (rid == invalidID) {
rid = newVirtualRegister(classKind);
DEBUG_ONLY(setMachineRegister(rid));
machineRegister[name].id = rid;
machineRegister[name].color = name;
}
return rid;
}
PreColoredRegister* getMachineRegistersBegin() const {return (PreColoredRegister*) machineRegister;} // FIX
PreColoredRegister* getMachineRegistersEnd() const {return (PreColoredRegister*) &machineRegister[6];} // FIX
// Return the VirtualRegister universe size.
Uint32 getSize() {return registerPool.getSize();}
void setSize(Uint32 size) {registerPool.setSize(size);}
};
#endif // _VIRTUAL_REGISTER_H_

View File

@@ -1,198 +0,0 @@
#! gmake
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (the "NPL"); you may not use this file except in
# compliance with the NPL. You may obtain a copy of the NPL at
# http://www.mozilla.org/NPL/
#
# Software distributed under the NPL is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
# for the specific language governing rights and limitations under the
# NPL.
#
# The Initial Developer of this code under the NPL is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1999 Netscape Communications Corporation. All Rights
# Reserved.
#
# Makefile for javascript in java.
#
# This makefile is intended for packaging releases, and probably isn't
# suitable for production use - it doesn't attempt to do understand
# java dependencies beyond the package level.
#
# The makefiles for the subdirectories included in this package are
# intended to be called by this makefile with the proper CLASSDIR,
# PATH_PREFIX etc. variables. Makefiles in subdirectories are
# actually executed in the toplevel directory, with the PATH_PREFIX
# variable set to the subdirectory where the makefile is located.
#
# Initial version courtesy Mike Ang.
# Next version by Mike McCabe
# Don't include SHELL define (per GNU manual recommendation) because it
# breaks WinNT (with GNU make) builds.
# SHELL = /bin/sh
# Some things we might want to tweek.
CLASSDIR = classes
PACKAGE_NAME = org.mozilla.javascript
PACKAGE_PATH = org/mozilla/javascript
# jar filenames and the directories that build them.
JS_JAR = js.jar
JS_DIR = $(PACKAGE_PATH)
JSTOOLS_JAR = jstools.jar
JSTOOLS_DIR = $(PACKAGE_PATH)/tools
JARS = $(JS_JAR) $(JSTOOLS_JAR)
# It's not polite to store toplevel files in a tarball or zip files.
# What is the name of the toplevel directory to store files in?
# XXX we should probably add versioning to this.
DIST_DIR = jsjava
# XXX test this with sj
JAVAC = javac
# We don't define JFLAGS but we do export it to child
# builds in case it's defined by the environment.
# To build optimized (with javac) say 'make JFLAGS=-O'
GZIP = gzip
ZIP = zip
UNZIP = unzip
# Shouldn't need to change anything below here.
# For Windows NT builds (under GNU make).
ifeq ($(OS_TARGET), WINNT)
CLASSPATHSEP = '\\;'
else
CLASSPATHSEP = :
endif
# Make compatibility - use these instead of gmake 'export VARIABLE'
EXPORTS = CLASSDIR=$(CLASSDIR) JAVAC=$(JAVAC) JFLAGS=$(JFLAGS) SHELL=$(SHELL) \
PACKAGE_PATH=$(PACKAGE_PATH) PACKAGE_NAME=$(PACKAGE_NAME)
helpmessage : FORCE
@echo 'Targets include:'
@echo '\tall - make jars, examples'
@echo '\tjars - make js.jar, jstools.jar'
@echo '\tfast - quick-and-dirty "make jars", for development'
@echo '\texamples - build the .class files in the examples directory'
@echo '\tcheck - perform checks on the source.'
@echo '\tclean - remove intermediate files'
@echo '\tclobber - make clean, and remove .jar files'
@echo '\tzip - make a distribution .zip file'
@echo '\tzip-source - make a distribution .zip file, with source'
@echo '\ttar - make a distribution .tar.gz file'
@echo '\ttar-source - make a distribution .tar.gz, with source'
@echo
@echo 'Define OS_TARGET to "WINNT" to build on Windows NT with GNU make.'
@echo
all : jars examples
jars : $(JARS)
fast : fast_$(JS_JAR) $(JSTOOLS_JAR)
# Always call the sub-Makefile - which may decide that the jar is up to date.
$(JS_JAR) : FORCE
$(MAKE) -f $(JS_DIR)/Makefile JAR=$(@) $(EXPORTS) \
PATH_PREFIX=$(JS_DIR) \
CLASSPATH=.
fast_$(JS_JAR) :
$(MAKE) -f $(JS_DIR)/Makefile JAR=$(JS_JAR) $(EXPORTS) \
PATH_PREFIX=$(JS_DIR) \
CLASSPATH=. \
fast
$(JSTOOLS_JAR) : $(JS_JAR) FORCE
$(MAKE) -f $(JSTOOLS_DIR)/Makefile JAR=$(@) $(EXPORTS) \
PATH_PREFIX=$(JSTOOLS_DIR) \
CLASSPATH=./$(JS_JAR)$(CLASSPATHSEP).
examples : $(JS_JAR) FORCE
$(MAKE) -f examples/Makefile $(EXPORTS) \
PATH_PREFIX=examples \
CLASSPATH=./$(JS_JAR)
# We ask the subdirs to update their MANIFESTs
MANIFEST : FORCE
$(MAKE) -f $(JS_DIR)/Makefile JAR=$(JS_JAR) $(EXPORTS) \
PATH_PREFIX=$(JS_DIR) $(JS_DIR)/MANIFEST
$(MAKE) -f $(JSTOOLS_DIR)/Makefile JAR=$(JSTOOLS_JAR) $(EXPORTS) \
PATH_PREFIX=$(JSTOOLS_DIR) $(JSTOOLS_DIR)/MANIFEST
$(MAKE) -f examples/Makefile $(EXPORTS) \
PATH_PREFIX=examples examples/MANIFEST
# so ls below always has something to work on
touch MANIFEST
# examples/Makefile doesn't get included in the
# MANIFEST file, (which is used to create the non-source distribution) so
# we include it here.
cat examples/MANIFEST $(JS_DIR)/MANIFEST \
$(JSTOOLS_DIR)/MANIFEST \
| xargs ls MANIFEST README.html \
$(JARS) \
Makefile examples/Makefile \
> $(@)
# Make a MANIFEST file containing only the binaries and documentation.
# This could be abstracted further...
MANIFEST_binonly : MANIFEST
cat examples/MANIFEST \
| xargs ls $(JARS) README.html MANIFEST > MANIFEST
# A subroutine - not intended to be called from outside the makefile.
do_zip :
# Make sure we get a fresh one
- rm -r $(DIST_DIR)
- mkdir $(DIST_DIR)
- rm $(DIST_DIR).zip
cat MANIFEST | xargs $(ZIP) -0 -q $(DIST_DIR).zip
mv $(DIST_DIR).zip $(DIST_DIR)
cd $(DIST_DIR) ; \
$(UNZIP) -q $(DIST_DIR).zip ; \
rm $(DIST_DIR).zip
$(ZIP) -r -9 -q $(DIST_DIR).zip $(DIST_DIR)
- rm -r $(DIST_DIR)
zip : check jars examples MANIFEST_binonly do_zip
zip-source : check jars examples MANIFEST do_zip
# A subroutine - not intended to be called from outside the makefile.
do_tar :
- rm -r $(DIST_DIR)
- mkdir $(DIST_DIR)
- rm $(DIST_DIR).tar $(DIST_DIR).tar.gz
cat MANIFEST | xargs tar cf $(DIST_DIR).tar
mv $(DIST_DIR).tar $(DIST_DIR)
cd $(DIST_DIR) ; \
tar xf $(DIST_DIR).tar ; \
rm $(DIST_DIR).tar
tar cf $(DIST_DIR).tar $(DIST_DIR)
- rm -r $(DIST_DIR)
$(GZIP) -9 $(DIST_DIR).tar
tar: check jars examples MANIFEST_binonly do_tar
tar-source : check jars examples MANIFEST do_tar
# These commands just get passed to the respective sub-Makefiles.
clean clobber check:
$(MAKE) -f $(JS_DIR)/Makefile $(EXPORTS) JAR=$(JS_JAR) \
PATH_PREFIX=$(JS_DIR) $(@)
$(MAKE) -f $(JSTOOLS_DIR)/Makefile $(EXPORTS) JAR=$(JSTOOLS_JAR) \
PATH_PREFIX=$(JSTOOLS_DIR) $(@)
$(MAKE) -f examples/Makefile $(EXPORTS) PATH_PREFIX=examples $(@)
#emulate .PHONY
FORCE :

View File

@@ -1,13 +0,0 @@
<html>
<body>
<h1>
<span CLASS=LXRSHORTDESC>
Rhino: JavaScript in Java<p>
</span>
</h1>
<span CLASS=LXRLONGDESC>
Rhino is an implementation of JavaScript in Java. Documentation can be found
<a href="http://www.mozilla.org/js/rhino/rhino.html">here</a>.
</span>
</body>
</html>

View File

@@ -1,94 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
import org.mozilla.javascript.*;
/**
* Example of controlling the JavaScript execution engine.
*
* We evaluate a script and then manipulate the result.
*
*/
public class Control {
/**
* Main entry point.
*
* Process arguments as would a normal Java program. Also
* create a new Context and associate it with the current thread.
* Then set up the execution environment and begin to
* execute scripts.
*/
public static void main(String[] args) {
Context cx = Context.enter();
// Set version to JavaScript1.2 so that we get object-literal style
// printing instead of "[object Object]"
cx.setLanguageVersion(Context.VERSION_1_2);
// Initialize the standard objects (Object, Function, etc.)
// This must be done before scripts can be executed.
Scriptable scope = cx.initStandardObjects(null);
// Now we can evaluate a script. Let's create a new object
// using the object literal notation.
Object result = null;
try {
result = cx.evaluateString(scope, "obj = {a:1, b:['x','y']}",
"MySource", 1, null);
}
catch (JavaScriptException jse) {
// ignore
}
FlattenedObject global = new FlattenedObject(scope);
FlattenedObject f = (FlattenedObject) global.getProperty("obj");
// Should print "obj == result" (Since the result of an assignment
// expression is the value that was assigned)
System.out.println("obj " + (f.getObject() == result ? "==" : "!=") +
" result");
// Should print "f.a == 1"
System.out.println("f.a == " + f.getProperty("a"));
FlattenedObject b = (FlattenedObject) f.getProperty("b");
// Should print "f.b[0] == x"
System.out.println("f.b[0] == " + b.getProperty(new Integer(0)));
// Should print "f.b[1] == y"
System.out.println("f.b[1] == " + b.getProperty(new Integer(1)));
try {
// Should print {a:1, b:["x", "y"]}
System.out.println(f.callMethod("toString", new Object[0]));
} catch (PropertyException e) {
// ignore
} catch (NotAFunctionException e) {
// ignore
} catch (JavaScriptException e) {
// ignore
}
cx.exit();
}
}

View File

@@ -1,359 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
import org.mozilla.javascript.*;
import java.io.*;
import java.util.Vector;
/**
* Define a simple JavaScript File object.
*
* This isn't intended to be any sort of definitive attempt at a
* standard File object for JavaScript, but instead is an example
* of a more involved definition of a host object.
*
* Example of use of the File object:
* <pre>
* js> defineClass("File")
* js> var file = new File("myfile.txt");
* js> file = new File("myfile.txt");
* [object File]
* js> file.writeLine("one"); <i>only now is file actually opened</i>
* js> file.writeLine("two");
* js> file.writeLine("thr", "ee");
* js> file.close(); <i>must close file before we can reopen for reading</i>
* js> var a = file.readLines(); <i>creates and fills an array with the contents of the file</i>
* js> a;
* one,two,three
* js>
* </pre>
*
*
* File errors or end-of-file signaled by thrown Java exceptions will
* be wrapped as JavaScript exceptions when called from JavaScript,
* and may be caught within JavaScript.
*
* @author Norris Boyd
*/
public class File extends ScriptableObject {
/**
* The zero-parameter constructor.
*
* When Context.defineClass is called with this class, it will
* construct File.prototype using this constructor.
*/
public File() {
}
/**
* The Java method defining the JavaScript File constructor.
*
* If the constructor has one or more arguments, and the
* first argument is not undefined, the argument is converted
* to a string as used as the filename.<p>
*
* Otherwise System.in or System.out is assumed as appropriate
* to the use.
*/
public static Scriptable js_File(Context cx, Object[] args,
Function ctorObj, boolean inNewExpr)
{
File result = new File();
if (args.length == 0 || args[0] == Context.getUndefinedValue()) {
result.name = "";
result.file = null;
} else {
result.name = Context.toString(args[0]);
result.file = new java.io.File(result.name);
}
return result;
}
/**
* Returns the name of this JavaScript class, "File".
*/
public String getClassName() {
return "File";
}
/**
* Get the name of the file.
*
* Used to define the "name" property.
*/
public String js_getName() {
return name;
}
/**
* Read the remaining lines in the file and return them in an array.
*
* Implements a JavaScript function.<p>
*
* This is a good example of creating a new array and setting
* elements in that array.
*
* @exception IOException if an error occurred while accessing the file
* associated with this object
* @exception JavaScriptException if a JavaScript exception occurred
* while creating the result array
*/
public Object js_readLines()
throws IOException, JavaScriptException
{
Vector v = new Vector();
String s;
while ((s = js_readLine()) != null) {
v.addElement(s);
}
Object[] lines = new Object[v.size()];
v.copyInto(lines);
Scriptable scope = ScriptableObject.getTopLevelScope(this);
Scriptable result;
try {
Context cx = Context.getCurrentContext();
result = cx.newObject(scope, "Array", lines);
} catch (PropertyException e) {
throw Context.reportRuntimeError(e.getMessage());
} catch (NotAFunctionException e) {
throw Context.reportRuntimeError(e.getMessage());
}
return result;
}
/**
* Read a line.
*
* Implements a JavaScript function.
* @exception IOException if an error occurred while accessing the file
* associated with this object, or EOFException if the object
* reached the end of the file
*/
public String js_readLine() throws IOException {
return getReader().readLine();
}
/**
* Read a character.
*
* @exception IOException if an error occurred while accessing the file
* associated with this object, or EOFException if the object
* reached the end of the file
*/
public String js_readChar() throws IOException {
int i = getReader().read();
if (i == -1)
return null;
char[] charArray = { (char) i };
return new String(charArray);
}
/**
* Read a block.
*
* @exception IOException if an error occurred while accessing the file
* associated with this object, or EOFException if the object
* reached the end of the file
*/
public String js_readBlock() throws IOException {
int i = getReader().read();
if (i == -1)
return null;
char[] charArray = { (char) i };
return new String(charArray);
}
/**
* Write strings.
*
* Implements a JavaScript function. <p>
*
* This function takes a variable number of arguments, converts
* each argument to a string, and writes that string to the file.
* @exception IOException if an error occurred while accessing the file
* associated with this object
*/
public static void js_write(Context cx, Scriptable thisObj,
Object[] args, Function funObj)
throws IOException
{
write0(thisObj, args, false);
}
/**
* Write strings and a newline.
*
* Implements a JavaScript function.
* @exception IOException if an error occurred while accessing the file
* associated with this object
*
*/
public static void js_writeLine(Context cx, Scriptable thisObj,
Object[] args, Function funObj)
throws IOException
{
write0(thisObj, args, true);
}
public int js_getLineNumber()
throws FileNotFoundException
{
return getReader().getLineNumber();
}
/**
* Close the file. It may be reopened.
*
* Implements a JavaScript function.
* @exception IOException if an error occurred while accessing the file
* associated with this object
*/
public void js_close() throws IOException {
if (reader != null) {
reader.close();
reader = null;
} else if (writer != null) {
writer.close();
writer = null;
}
}
/**
* Finalizer.
*
* Close the file when this object is collected.
*/
public void finalize() {
try {
js_close();
}
catch (IOException e) {
}
}
/**
* Get the Java reader.
*
* Note that we use the name "jsFunction_getReader" because if we
* used just "js_getReader" we'd be defining a readonly property
* named "reader".
*
*/
public Object jsFunction_getReader() {
if (reader == null)
return null;
// Here we use toObject() to "wrap" the BufferedReader object
// in a Scriptable object so that it can be manipulated by
// JavaScript.
Scriptable parent = ScriptableObject.getTopLevelScope(this);
return Context.toObject(reader, parent);
}
/**
* Get the Java writer.
*
* @see File#jsFunction_getReader
*
*/
public Object jsFunction_getWriter() {
if (writer == null)
return null;
Scriptable parent = ScriptableObject.getTopLevelScope(this);
return Context.toObject(writer, parent);
}
/**
* Get the reader, checking that we're not already writing this file.
*/
private LineNumberReader getReader() throws FileNotFoundException {
if (writer != null) {
throw Context.reportRuntimeError("already writing file \""
+ name
+ "\"");
}
if (reader == null)
reader = new LineNumberReader(file == null
? new InputStreamReader(System.in)
: new FileReader(file));
return reader;
}
/**
* Perform the guts of write and writeLine.
*
* Since the two functions differ only in whether they write a
* newline character, move the code into a common subroutine.
*
*/
private static void write0(Scriptable thisObj, Object[] args, boolean eol)
throws IOException
{
File thisFile = checkInstance(thisObj);
if (thisFile.reader != null) {
throw Context.reportRuntimeError("already writing file \""
+ thisFile.name
+ "\"");
}
if (thisFile.writer == null)
thisFile.writer = new BufferedWriter(
thisFile.file == null ? new OutputStreamWriter(System.out)
: new FileWriter(thisFile.file));
for (int i=0; i < args.length; i++) {
String s = Context.toString(args[i]);
thisFile.writer.write(s, 0, s.length());
}
if (eol)
thisFile.writer.newLine();
}
/**
* Perform the instanceof check and return the downcasted File object.
*
* This is necessary since methods may reside in the File.prototype
* object and scripts can dynamically alter prototype chains. For example:
* <pre>
* js> defineClass("File");
* js> o = {};
* [object Object]
* js> o.__proto__ = File.prototype;
* [object File]
* js> o.write("hi");
* js: called on incompatible object
* </pre>
* The runtime will take care of such checks when non-static Java methods
* are defined as JavaScript functions.
*/
private static File checkInstance(Scriptable obj) {
if (obj == null || !(obj instanceof File)) {
throw Context.reportRuntimeError("called on incompatible object");
}
return (File) obj;
}
/**
* Some private data for this class.
*/
private String name;
private java.io.File file; // may be null, meaning to use System.out or .in
private LineNumberReader reader;
private BufferedWriter writer;
}

View File

@@ -1,152 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
import org.mozilla.javascript.*;
/**
* An example host object class.
*
* Here's a shell session showing the Foo object in action:
* <pre>
* js> defineClass("Foo")
* js> foo = new Foo(); <i>A constructor call, see <a href="#Foo">Foo</a> below.</i>
* [object Foo] <i>The "Foo" here comes from <a href"#getClassName">getClassName</a>.</i>
* js> foo.counter; <i>The counter property is defined by the <code>defineProperty</code></i>
* 0 <i>call below and implemented by the <a href="#getCounter">getCounter</a></i>
* js> foo.counter; <i>method below.</i>
* 1
* js> foo.counter;
* 2
* js> foo.resetCounter(); <i>Results in a call to <a href="#resetCounter">resetCounter</a>.</i>
* js> foo.counter; <i>Now the counter has been reset.</i>
* 0
* js> foo.counter;
* 1
* js> bar = new Foo(37); <i>Create a new instance.</i>
* [object Foo]
* js> bar.counter; <i>This instance's counter is distinct from</i>
* 37 <i>the other instance's counter.</i>
* js> foo.varargs(3, "hi"); <i>Calls <a href="#varargs">varargs</a>.</i>
* this = [object Foo]; args = [3, hi]
* js> foo[7] = 34; <i>Since we extended ScriptableObject, we get</i>
* 34 <i>all the behavior of a JavaScript object</i>
* js> foo.a = 23; <i>for free.</i>
* 23
* js> foo.a + foo[7];
* 57
* js>
* </pre>
*
* @see org.mozilla.javascript.Context
* @see org.mozilla.javascript.Scriptable
* @see org.mozilla.javascript.ScriptableObject
*
* @author Norris Boyd
*/
public class Foo extends ScriptableObject {
/**
* The zero-parameter constructor.
*
* When Context.defineClass is called with this class, it will
* construct Foo.prototype using this constructor.
*/
public Foo() {
}
/**
* The Java method defining the JavaScript Foo constructor.
*
* Takes an initial value for the counter property.
* Note that in the example Shell session above, we didn't
* supply a argument to the Foo constructor. This means that
* the Undefined value is used as the value of the argument,
* and when the argument is converted to an integer, Undefined
* becomes 0.
*/
public Foo(int counterStart) {
counter = counterStart;
}
/**
* Returns the name of this JavaScript class, "Foo".
*/
public String getClassName() {
return "Foo";
}
/**
* The Java method defining the JavaScript resetCounter function.
*
* Resets the counter to 0.
*/
public void jsFunction_resetCounter() {
counter = 0;
}
/**
* The Java method implementing the getter for the counter property.
* <p>
* If "setCounter" had been defined in this class, the runtime would
* call the setter when the property is assigned to.
*/
public int jsGet_counter() {
return counter++;
}
/**
* An example of a variable-arguments method.
*
* All variable arguments methods must have the same number and
* types of parameters, and must be static. <p>
* @param cx the Context of the current thread
* @param thisObj the JavaScript 'this' value.
* @param args the array of arguments for this call
* @param funObj the function object of the invoked JavaScript function
* This value is useful to compute a scope using
* Context.getTopLevelScope().
* @return computes the string values and types of 'this' and
* of each of the supplied arguments and returns them in a string.
*
* @exception ThreadAssociationException if the current
* thread is not associated with a Context
* @see org.mozilla.javascript.ScriptableObject#getTopLevelScope
*/
public static Object jsFunction_varargs(Context cx, Scriptable thisObj,
Object[] args, Function funObj)
{
StringBuffer buf = new StringBuffer();
buf.append("this = ");
buf.append(Context.toString(thisObj));
buf.append("; args = [");
for (int i=0; i < args.length; i++) {
buf.append(Context.toString(args[i]));
if (i+1 != args.length)
buf.append(", ");
}
buf.append("]");
return buf.toString();
}
/**
* A piece of private data for this class.
*/
private int counter;
}

View File

@@ -1,43 +0,0 @@
#! gmake
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (the "NPL"); you may not use this file except in
# compliance with the NPL. You may obtain a copy of the NPL at
# http://www.mozilla.org/NPL/
#
# Software distributed under the NPL is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
# for the specific language governing rights and limitations under the
# NPL.
#
# The Initial Developer of this code under the NPL is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1999 Netscape Communications Corporation. All Rights
# Reserved.
#
# Makefile for the examples directory.
#
# This Makefile just calls $(JAVAC) on all the .java files. This
# Makefile is intended to be called from the toplevel Makefile.
#
JSFILES = $(PATH_PREFIX)/*.js
SOURCES = $(PATH_PREFIX)/*.java
CLASSES = $(PATH_PREFIX)/*.class
$(CLASSES) : $(SOURCES)
$(JAVAC) $(JFLAGS) $(SOURCES)
clean :
- rm $(CLASSES) $(PATH_PREFIX)/MANIFEST
clobber : clean
check :
$(PATH_PREFIX)/MANIFEST : $(SOURCES) $(CLASSES) $(JSFILES)
ls $(SOURCES) $(CLASSES) $(JSFILES) \
> $(@)
# Emulate .PHONY
FORCE :

View File

@@ -1,258 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
import org.mozilla.javascript.*;
import java.util.Vector;
/**
* Matrix: An example host object class that implements the Scriptable interface.
*
* Built-in JavaScript arrays don't handle multiple dimensions gracefully: the
* script writer must create every array in an array of arrays. The Matrix class
* takes care of that by automatically allocating arrays for every index that
* is accessed. What's more, the Matrix constructor takes a integer argument
* that specifies the dimension of the Matrix. If m is a Matrix with dimension 3,
* then m[0] will be a Matrix with dimension 1, and m[0][0] will be an Array.
*
* Here's a shell session showing the Matrix object in action:
* <pre>
* js> defineClass("Matrix")
* js> m = new Matrix(2); <i>A constructor call, see <a href="#Matrix">Matrix</a> below.</i>
* [object Matrix] <i>The "Matrix" here comes from <a href"#getClassName">getClassName</a>.</i>
* js> version(120); <i>switch to JavaScript1.2 to see arrays better</i>
* 0
* js> m[0][0] = 3;
* 3
* js> m[0]; <i>an array was created automatically!</i>
* [3]
* js> m[1]; <i>array is created even if we don't set a value</i>
* []
* js> m.dim; <i>we can access the "dim" property</i>
* 2
* js> m.dim = 3;
* 3
* js> m.dim; <i>but not modify it</i>
* 2
* </pre>
*
* @see org.mozilla.javascript.Context
* @see org.mozilla.javascript.Scriptable
*
* @author Norris Boyd
*/
public class Matrix implements Scriptable {
/**
* The zero-parameter constructor.
*
* When Context.defineClass is called with this class, it will
* construct Matrix.prototype using this constructor.
*/
public Matrix() {
}
/**
* The Java constructor, also used to define the JavaScript constructor.
*/
public Matrix(int dimension) {
if (dimension <= 0) {
throw Context.reportRuntimeError(
"Dimension of Matrix must be greater than zero");
}
dim = dimension;
v = new Vector();
}
/**
* Returns the name of this JavaScript class, "Matrix".
*/
public String getClassName() {
return "Matrix";
}
/**
* Defines the "dim" property by returning true if name is
* equal to "dim".
* <p>
* Defines no other properties, i.e., returns false for
* all other names.
*
* @param name the name of the property
* @param start the object where lookup began
*/
public boolean has(String name, Scriptable start) {
return name.equals("dim");
}
/**
* Defines all numeric properties by returning true.
*
* @param index the index of the property
* @param start the object where lookup began
*/
public boolean has(int index, Scriptable start) {
return true;
}
/**
* Get the named property.
* <p>
* Handles the "dim" property and returns NOT_FOUND for all
* other names.
* @param name the property name
* @param start the object where the lookup began
*/
public Object get(String name, Scriptable start) {
if (name.equals("dim"))
return new Integer(dim);
return NOT_FOUND;
}
/**
* Get the indexed property.
* <p>
* Look up the element in the associated vector and return
* it if it exists. If it doesn't exist, create it.<p>
* @param index the index of the integral property
* @param start the object where the lookup began
*/
public Object get(int index, Scriptable start) {
if (index >= v.size())
v.setSize(index+1);
Object result = v.elementAt(index);
if (result != null)
return result;
if (dim > 2) {
Matrix m = new Matrix(dim-1);
m.setParentScope(getParentScope());
m.setPrototype(getPrototype());
result = m;
} else {
Context cx = Context.getCurrentContext();
Scriptable scope = ScriptableObject.getTopLevelScope(start);
result = cx.newArray(scope, 0);
}
v.setElementAt(result, index);
return result;
}
/**
* Set a named property.
*
* We do nothing here, so all properties are effectively read-only.
*/
public void put(String name, Scriptable start, Object value) {
}
/**
* Set an indexed property.
*
* We do nothing here, so all properties are effectively read-only.
*/
public void put(int index, Scriptable start, Object value) {
}
/**
* Remove a named property.
*
* This method shouldn't even be called since we define all properties
* as PERMANENT.
*/
public void delete(String id) {
}
/**
* Remove an indexed property.
*
* This method shouldn't even be called since we define all properties
* as PERMANENT.
*/
public void delete(int index) {
}
/**
* Get prototype.
*/
public Scriptable getPrototype() {
return prototype;
}
/**
* Set prototype.
*/
public void setPrototype(Scriptable prototype) {
this.prototype = prototype;
}
/**
* Get parent.
*/
public Scriptable getParentScope() {
return parent;
}
/**
* Set parent.
*/
public void setParentScope(Scriptable parent) {
this.parent = parent;
}
/**
* Get properties.
*
* We return an empty array since we define all properties to be DONTENUM.
*/
public Object[] getIds() {
return new Object[0];
}
/**
* Default value.
*
* Use the convenience method from Context that takes care of calling
* toString, etc.
*/
public Object getDefaultValue(Class typeHint) {
return "[object Matrix]";
}
/**
* instanceof operator.
*
* We mimick the normal JavaScript instanceof semantics, returning
* true if <code>this</code> appears in <code>value</code>'s prototype
* chain.
*/
public boolean hasInstance(Scriptable value) {
Scriptable proto = value.getPrototype();
while (proto != null) {
if (proto.equals(this)) return true;
}
return false;
}
/**
* Some private data for this class.
*/
private int dim;
private Vector v;
private Scriptable prototype, parent;
}

View File

@@ -1,57 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
import org.mozilla.javascript.*;
/**
* RunScript: simplest example of controlling execution of Rhino.
*
* Collects its arguments from the command line, executes the
* script, and prints the result.
*
* @author Norris Boyd
*/
public class RunScript {
public static void main(String args[])
throws JavaScriptException
{
// Creates and enters a Context. The Context stores information
// about the execution environment of a script.
Context cx = Context.enter();
// Initialize the standard objects (Object, Function, etc.)
// This must be done before scripts can be executed. Returns
// a scope object that we use in later calls.
Scriptable scope = cx.initStandardObjects(null);
// Collect the arguments into a single string.
String s = "";
for (int i=0; i < args.length; i++)
s += args[i];
// Now evaluate the string we've colected.
Object result = cx.evaluateString(scope, s, "<cmd>", 1, null);
// Convert the result to a string and print it.
System.err.println(cx.toString(result));
// Exit from the context.
Context.exit();
}
}

View File

@@ -1,336 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
import org.mozilla.javascript.*;
import java.io.*;
/**
* The shell program.
*
* Can execute scripts interactively or in batch mode at the command line.
* An example of controlling the JavaScript engine.
*
* @author Norris Boyd
*/
public class Shell extends ScriptableObject {
/**
* Main entry point.
*
* Process arguments as would a normal Java program. Also
* create a new Context and associate it with the current thread.
* Then set up the execution environment and begin to
* execute scripts.
*/
public static void main(String args[]) {
// Associate a new Context with this thread
Context cx = Context.enter();
// A bit of shorthand: since Shell extends ScriptableObject,
// we can make it the global object.
global = new Shell();
// Initialize the standard objects (Object, Function, etc.)
// This must be done before scripts can be executed.
cx.initStandardObjects(global);
// Define some global functions particular to the shell. Note
// that these functions are not part of ECMA.
String[] names = { "print", "quit", "version", "load", "help" };
try {
global.defineFunctionProperties(names, Shell.class,
ScriptableObject.DONTENUM);
} catch (PropertyException e) {
throw new Error(e.getMessage());
}
args = processOptions(cx, args);
// Set up "arguments" in the global scope to contain the command
// line arguments after the name of the script to execute
Object[] array = args;
if (args.length > 0) {
int length = args.length - 1;
array = new Object[length];
System.arraycopy(args, 1, array, 0, length);
}
Scriptable argsObj = cx.newArray(global, array);
global.defineProperty("arguments", argsObj,
ScriptableObject.DONTENUM);
processSource(cx, args.length == 0 ? null : args[0]);
cx.exit();
}
/**
* Parse arguments.
*/
public static String[] processOptions(Context cx, String args[]) {
for (int i=0; i < args.length; i++) {
String arg = args[i];
if (!arg.startsWith("-")) {
String[] result = new String[args.length - i];
for (int j=i; j < args.length; j++)
result[j-i] = args[j];
return result;
}
if (arg.equals("-version")) {
if (++i == args.length)
usage(arg);
double d = cx.toNumber(args[i]);
if (d != d)
usage(arg);
cx.setLanguageVersion((int) d);
continue;
}
usage(arg);
}
return new String[0];
}
/**
* Return name of this class, the global object.
*
* This method must be implemented in all concrete classes
* extending ScriptableObject.
*
* @see com.netscape.javascript.Scriptable#getClassName
*/
public String getClassName() {
return "global";
}
/**
* Print a usage message.
*/
public static void usage(String s) {
p("Didn't understand \"" + s + "\".");
p("Valid arguments are:");
p("-version 100|110|120|130");
System.exit(1);
}
/**
* Print a help message.
*
* This method is defined as a JavaScript function.
*/
public static void help(String s) {
p("");
p("Command Description");
p("======= ===========");
p("help() Display usage and help messages. ");
p("defineClass(className) Define an extension using the Java class");
p(" named with the string argument. ");
p(" Uses ScriptableObject.defineClass(). ");
p("load(['foo.js', ...]) Load JavaScript source files named by ");
p(" string arguments. ");
p("loadClass(className) Load a class named by a string argument.");
p(" The class must be a script compiled to a");
p(" class file. ");
p("print([expr ...]) Evaluate and print expressions. ");
p("quit() Quit the shell. ");
p("version([number]) Get or set the JavaScript version number.");
p("");
}
/**
* Print the string values of its arguments.
*
* This method is defined as a JavaScript function.
* Note that its arguments are of the "varargs" form, which
* allows it to handle an arbitrary number of arguments
* supplied to the JavaScript function.
*
*/
public static void print(Context cx, Scriptable thisObj,
Object[] args, Function funObj)
{
for (int i=0; i < args.length; i++) {
if (i > 0)
System.out.print(" ");
// Convert the arbitrary JavaScript value into a string form.
String s = Context.toString(args[i]);
System.out.print(s);
}
System.out.println();
}
/**
* Quit the shell.
*
* This only affects the interactive mode.
*
* This method is defined as a JavaScript function.
*/
public static void quit() {
quitting = true;
}
/**
* Get and set the language version.
*
* This method is defined as a JavaScript function.
*/
public static double version(Context cx, Scriptable thisObj,
Object[] args, Function funObj)
{
double result = (double) cx.getLanguageVersion();
if (args.length > 0) {
double d = cx.toNumber(args[0]);
cx.setLanguageVersion((int) d);
}
return result;
}
/**
* Load and execute a set of JavaScript source files.
*
* This method is defined as a JavaScript function.
*
*/
public static void load(Context cx, Scriptable thisObj,
Object[] args, Function funObj)
{
for (int i=0; i < args.length; i++) {
processSource(cx, cx.toString(args[i]));
}
}
/**
* Evaluate JavaScript source.
*
* @param cx the current context
* @param filename the name of the file to compile, or null
* for interactive mode.
*/
public static void processSource(Context cx, String filename) {
if (filename == null) {
BufferedReader in = new BufferedReader
(new InputStreamReader(System.in));
String sourceName = "<stdin>";
int lineno = 1;
boolean hitEOF = false;
do {
int startline = lineno;
System.err.print("js> ");
System.err.flush();
try {
String source = "";
// Collect lines of source to compile.
while(true) {
String newline;
newline = in.readLine();
if (newline == null) {
hitEOF = true;
break;
}
source = source + newline + "\n";
lineno++;
// Continue collecting as long as more lines
// are needed to complete the current
// statement. stringIsCompilableUnit is also
// true if the source statement will result in
// any error other than one that might be
// resolved by appending more source.
if (cx.stringIsCompilableUnit(source))
break;
}
Object result = cx.evaluateString(global, source,
sourceName, startline,
null);
if (result != cx.getUndefinedValue()) {
System.err.println(cx.toString(result));
}
}
catch (WrappedException we) {
// Some form of exception was caught by JavaScript and
// propagated up.
System.err.println(we.getWrappedException().toString());
we.printStackTrace();
}
catch (EvaluatorException ee) {
// Some form of JavaScript error.
System.err.println("js: " + ee.getMessage());
}
catch (JavaScriptException jse) {
// Some form of JavaScript error.
System.err.println("js: " + jse.getMessage());
}
catch (IOException ioe) {
System.err.println(ioe.toString());
}
if (quitting) {
// The user executed the quit() function.
break;
}
} while (!hitEOF);
System.err.println();
} else {
FileReader in = null;
try {
in = new FileReader(filename);
}
catch (FileNotFoundException ex) {
Context.reportError("Couldn't open file \"" + filename + "\".");
return;
}
try {
// Here we evalute the entire contents of the file as
// a script. Text is printed only if the print() function
// is called.
cx.evaluateReader(global, in, filename, 1, null);
}
catch (WrappedException we) {
System.err.println(we.getWrappedException().toString());
we.printStackTrace();
}
catch (EvaluatorException ee) {
System.err.println("js: " + ee.getMessage());
}
catch (JavaScriptException jse) {
System.err.println("js: " + jse.getMessage());
}
catch (IOException ioe) {
System.err.println(ioe.toString());
}
finally {
try {
in.close();
}
catch (IOException ioe) {
System.err.println(ioe.toString());
}
}
}
System.gc();
}
private static void p(String s) {
System.out.println(s);
}
static Shell global;
static boolean quitting;
}

View File

@@ -1,99 +0,0 @@
/**
* checkParam.js
*
* The files given as arguments on the command line are assumed to be
* Java source code files. This program checks to see that the @param
* tags in the documentation comments match with the parameters for
* the associated Java methods.
* <p>
* Any errors found are reported.
*
*/
defineClass("File")
// Return true if "str" ends with "suffix".
function stringEndsWith(str, suffix) {
return str.substring(str.length - suffix.length) == suffix;
}
/**
* Perform processing once the end of a documentation comment is seen.
*
* Look for a parameter list following the end of the comment and
* collect the parameters and compare to the @param entries.
* Report any discrepancies.
* @param f the current file
* @param a an array of parameters from @param comments
* @param line the string containing the comment end (in case the
* parameters are on the same line)
*/
function processCommentEnd(f, a, line) {
while (line != null && !line.match(/\(/))
line = f.readLine();
while (line != null && !line.match(/\)/))
line += f.readLine();
if (line === null)
return;
var m = line.match(/\(([^\)]+)\)/);
var args = m ? m[1].split(",") : [];
if (a.length != args.length) {
print('"' + f.name +
'"; line ' + f.lineNumber +
' mismatch: had a different number' +
' of @param entries and parameters.');
} else {
for (var i=0; i < a.length; i++) {
if (!stringEndsWith(args[i], a[i])) {
print('"' + f.name +
'"; line ' + f.lineNumber +
' mismatch: had "' + a[i] +
'" and "' + args[i] + '".');
break;
}
}
}
}
/**
* Process the given file, looking for mismatched @param lists and
* parameter lists.
* @param f the file to process
*/
function processFile(f) {
var line;
var m;
var i = 0;
var a = [];
outer:
while ((line = f.readLine()) != null) {
if (line.match(/@param/)) {
while (m = line.match(/@param[ ]+([^ ]+)/)) {
a[i++] = m[1];
line = f.readLine();
if (line == null)
break outer;
}
}
if (i != 0 && line.match(/\*\//)) {
processCommentEnd(f, a, line);
i = 0;
a = [];
}
}
if (i != 0) {
print('"' + f.name +
'"; line ' + f.lineNumber +
' missing parameters at end of file.');
}
}
// main script: process each file in arguments list
for (var i=0; i < arguments.length; i++) {
var filename = String(arguments[i]);
print("Checking " + filename + "...");
var f = new File(filename);
processFile(f);
}
print("done.");

View File

@@ -1,35 +0,0 @@
/*
enum.js
Implementing the interface java.util.Enumeration using the new syntax.
Note that this syntax is experimental only, and hasn't been approved
by ECMA.
The same functionality can be had without the new syntax using the
uglier syntax:
var elements = new JavaAdapter(java.util.Enumeration, {
index: 0, elements: array,
hasMoreElements: function ...
nextElement: function ...
});
by Patrick C. Beard.
*/
// an array to enumerate.
var array = [0, 1, 2];
// create an array enumeration.
var elements = new java.util.Enumeration() {
index: 0, elements: array,
hasMoreElements: function() {
return (this.index < this.elements.length);
},
nextElement: function() {
return this.elements[this.index++];
}
};
// now print out the array by enumerating through the Enumeration
while (elements.hasMoreElements())
print(elements.nextElement());

View File

@@ -1,472 +0,0 @@
/**
* Process a JavaScript source file and process special comments
* to produce an HTML file of documentation, similar to javadoc.
* @author Norris Boyd
* @see rhinotip.jar
* @lastmodified xx
* @version 1.2 Roland Pennings: Allow multiple files for a function.
* @version 1.3 Roland Pennings: Removes ../.. from the input directory name
*/
defineClass("File")
var functionDocArray = [];
var inputDirName = "";
var indexFileArray = [];
var indexFile = "";
var indexFileName = "index_files";
var indexFunctionArray = [];
var indexFunction = "";
var indexFunctionName = "index_functions";
var FileList = [];
var DirList = [];
var outputdir = null;
var debug = 0;
/**
* Process JavaScript source file <code>f</code>, writing jsdoc to
* file <code>out</code>.
* @param f input file
* @param fname name of the input file (without the path)
* @param inputdir directory of the input file
* @param out output file
*/
function processFile(f, fname, inputdir, out) {
var s;
var firstLine = true;
indexFileArray[fname] = "";
// write the header of the output file
out.writeLine('<HTML><HEADER><TITLE>' + fname + '</TITLE><BODY>');
if (inputdir != null) {
outstr = '<a name=\"_top_\"></a><pre><a href=\"' + indexFile + '\">Index Files</a> ';
outstr += '<a href=\"' + indexFunction + '\">Index Functions</a></pre><hr>';
out.writeLine(outstr);
}
// process the input file
var comment = "";
while ((s = f.readLine()) != null) {
var m = s.match(/\/\*\*(.*)/);
if (m != null) {
// Found a comment start.
s = "*" + m[1];
do {
m = s.match(/(.*)\*\//);
if (m != null) {
// Found end of comment.
comment += m[1];
break;
}
// Strip leading whitespace and "*".
comment += s.replace(/^\s*\*/, "");
s = f.readLine();
} while (s != null);
if (debug)
print("Found comment " + comment);
if (firstLine) {
// We have a comment for the whole file.
out.writeLine('<H1>File ' + fname + '</H1>');
out.writeLine(processComment(comment,firstLine,fname));
out.writeLine('<HR>');
firstLine = false;
comment = "";
continue;
}
}
// match the beginning of the function
// NB we also match functions without a comment!
// if we have two comments one after another only the last one will be taken
m = s.match(/^\s*function\s+((\w+)|(\w+)(\s+))\(([^)]*)\)/);
if (m != null)
{
// Found a function start
var htmlText = processFunction(m[1], m[2], comment);
// Save the text in a global variable, so we
// can write out a table of contents first.
functionDocArray[functionDocArray.length] =
{name:m[1], text:htmlText};
// Store the function also in the indexFunctionArray
// so we can have a seperate file with the function table of contents
if (indexFunctionArray[m[1]]) {
// print("ERROR: function: " + m[1] + " is defined more than once!");
// Allow multiple files for a function
with (indexFunctionArray[m[1]]) {
filename = filename + "|" + fname;
// print("filename = " + filename);
}
} else {
indexFunctionArray[m[1]] =
{filename:fname};
}
//reset comment
comment = "";
}
firstLine = false;
}
// Write table of contents.
for (var i=0; i < functionDocArray.length; i++) {
with (functionDocArray[i]) {
out.writeLine('function <A HREF=#' + name +
'>' + name + '</A><BR>');
}
}
out.writeLine('<HR>');
// Now write the saved function documentation.
for (i=0; i < functionDocArray.length; i++) {
with (functionDocArray[i]) {
out.writeLine('<A NAME=' + name + '>');
out.writeLine(text);
}
}
out.writeLine('</BODY></HTML>');
// Now clean up the doc array
functionDocArray = [];
}
/**
* Process function and associated comment.
* @param name the name of the function
* @param args the args of the function as a single string
* @param comment the text of the comment
* @return a string for the HTML text of the documentation
*/
function processFunction(name, args, comment) {
if (debug)
print("Processing " + name + " " + args + " " + comment);
return "<H2>Function " + name + "</H2>" +
"<PRE>" +
"function " + name + "(" + args + ")" +
"</PRE>" +
processComment(comment,0,name) +
"<P><BR><BR>";
}
/**
* Process comment.
* @param comment the text of the comment
* @param firstLine shows if comment is at the beginning of the file
* @param fname name of the file (without path)
* @return a string for the HTML text of the documentation
*/
function processComment(comment,firstLine,fname) {
var tags = {};
// Use the "lambda" form of regular expression replace,
// where the replacement object is a function rather
// than a string. The function is called with the
// matched text and any parenthetical matches as
// arguments, and the result of the function used as the
// replacement text.
// Here we use the function to build up the "tags" object,
// which has a property for each "@" tag that is the name
// of the tag, and whose value is an array of the
// text following that tag.
comment = comment.replace(/@(\w+)\s+([^@]*)/g,
function (s, name, text) {
var a = tags[name] || [];
a.push(text);
tags[name] = a;
return "";
});
// if we have a comment at the beginning of a file
// store the comment for the index file
if (firstLine) {
indexFileArray[fname] = comment;
}
var out = comment + '<P>';
if (tags["param"]) {
// Create a table of parameters and their descriptions.
var array = tags["param"];
var params = "";
for (var i=0; i < array.length; i++) {
var m = array[i].match(/(\w+)\s+(.*)/);
params += '<TR><TD><I>'+m[1]+'</I></TD>' +
'<TD>'+m[2]+'</TD></TR>';
}
out += '<TABLE WIDTH="90%" BORDER=1>';
out += '<TR BGCOLOR=0xdddddddd>';
out += '<TD><B>Parameter</B></TD>';
out += '<TD><B>Description</B></TD></TR>';
out += params;
out += '</TABLE><P>';
}
if (tags["return"]) {
out += "<DT><B>Returns:</B><DD>";
out += tags["return"][0] + "</DL><P>";
}
if (tags["author"]) {
// List the authors together, separated by commas.
out += '<DT><B>Author:</B><DD>';
var array = tags["author"];
for (var i=0; i < array.length; i++) {
out += array[i];
if (i+1 < array.length)
out += ", ";
}
out += '</DL><P>';
}
if (tags["version"]) {
// Show the version.
out += '<DT><B>Version:</B><DD>';
var array = tags["version"];
for (var i=0; i < array.length; i++) {
out += array[i];
if (i+1 < array.length)
out += "<BR><DD>";
}
out += '</DL><P>';
}
if (tags["see"]) {
// List the see modules together, separated by <BR>.
out += '<DT><B>Dependencies:</B><DD>';
var array = tags["see"];
for (var i=0; i < array.length; i++) {
out += array[i];
if (i+1 < array.length)
out += "<BR><DD>";
}
out += '</DL><P>';
}
if (tags["lastmodified"]) {
// Shows a last modified description with client-side js.
out += '<DT><B>Last modified:</B><DD>';
out += '<script><!--\n';
out += 'document.writeln(document.lastModified);\n';
out += '// ---></script>\n';
out += '</DL><P>';
}
// additional tags can be added here (i.e., "if (tags["see"])...")
return out;
}
/**
* Create an html output file
* @param outputdir directory to put the file
* @param htmlfile name of the file
*/
function CreateOutputFile(outputdir,htmlfile)
{
if (outputdir==null)
{
var outname = htmlfile;
}
else
{
var separator = Packages.java.io.File.separator;
var outname = outputdir + separator + htmlfile.substring(htmlfile.lastIndexOf(separator),htmlfile.length);
}
print("output file: " + outname);
return new File(outname);
}
/**
* Process a javascript file. Puts the generated HTML file in the outdir
* @param filename name of the javascript file
* @inputdir input directory of the file (default null)
*/
function processJSFile(filename,inputdir)
{
if (debug) print("filename = " + filename + " inputdir = " + inputdir);
if (!filename.match(/\.js$/)) {
print("Expected filename to end in '.js'; had instead " +
filename + ". I don't treat the file.");
} else {
if (inputdir==null)
{
var inname = filename;
}
else
{
var separator = Packages.java.io.File.separator;
var inname = inputdir + separator + filename;
}
print("Processing file " + inname);
var f = new File(inname);
// create the output file
var htmlfile = filename.replace(/\.js$/, ".html");
var out = CreateOutputFile(outputdir,htmlfile);
processFile(f, filename, inputdir, out);
out.close();
}
}
/**
* Generate index files containing links to the processed javascript files
* and the generated functions
*/
function GenerateIndex(dirname)
{
// construct the files index file
var out = CreateOutputFile(outputdir,indexFile);
// write the beginning of the file
out.writeLine('<HTML><HEADER><TITLE>File Index - directory: ' + dirname + '</TITLE><BODY>');
out.writeLine('<H1>File Index - directory: ' + dirname + '</H1>\n');
out.writeLine('<TABLE WIDTH="90%" BORDER=1>');
out.writeLine('<TR BGCOLOR=0xdddddddd>');
out.writeLine('<TD><B>File</B></TD>');
out.writeLine('<TD><B>Description</B></TD></TR>');
var separator = Packages.java.io.File.separator;
// sort the index file array
var SortedFileArray = [];
for (var fname in indexFileArray)
SortedFileArray.push(fname);
SortedFileArray.sort();
for (var i=0; i < SortedFileArray.length; i++) {
var fname = SortedFileArray[i];
var htmlfile = fname.replace(/\.js$/, ".html");
out.writeLine('<TR><TD><A HREF=\"' + htmlfile + '\">' + fname + '</A></TD></TD><TD>');
if (indexFileArray[fname])
out.writeLine(indexFileArray[fname]);
else
out.writeLine('No comments');
out.writeLine('</TD></TR>\n');
}
out.writeLine('</TABLE></BODY></HTML>');
out.close();
// construct the functions index file
var out = CreateOutputFile(outputdir,indexFunction);
// write the beginning of the file
out.writeLine('<HTML><HEADER><TITLE>Function Index - directory: ' + dirname + '</TITLE><BODY>');
out.writeLine('<H1>Function Index - directory: ' + dirname + '</H1>\n');
out.writeLine('<TABLE WIDTH="90%" BORDER=1>');
out.writeLine('<TR BGCOLOR=0xdddddddd>');
out.writeLine('<TD><B>Function</B></TD>');
out.writeLine('<TD><B>Files</B></TD></TR>');
// sort the function array
var SortedFunctionArray = [];
for (var functionname in indexFunctionArray)
SortedFunctionArray.push(functionname);
SortedFunctionArray.sort();
for (var j=0; j < SortedFunctionArray.length; j++) {
var funcname = SortedFunctionArray[j];
with (indexFunctionArray[funcname]) {
var outstr = '<TR><TD>' + funcname + '</TD><TD>';
var filelst = filename.split("|");
for (var i in filelst) {
var htmlfile = filelst[i].replace(/\.js$/, ".html");
outstr += '<A HREF=\"' + htmlfile + '#' + funcname + '\">' + filelst[i] + '</A>&nbsp;';
}
outstr += '</TD></TR>';
out.writeLine(outstr);
}
}
out.writeLine('</TABLE></BODY></HTML>');
out.close();
}
/**
* prints the options for JSDoc
*/
function PrintOptions()
{
print("You can use the following options:\n");
print("-d: specify an output directory for the generated html files\n");
print("-i: processes all files in an input directory (you can specify several directories)\n");
quit();
}
// Main Script
// first read the arguments
if (! arguments)
PrintOptions();
for (var i=0; i < arguments.length; i++) {
if (debug) print("argument: + \'" + arguments[i] + "\'");
if (arguments[i].match(/^\-/)) {
if (String(arguments[i])=="-d"){
// output directory for the generated html files
outputdir = String(arguments[i+1]);
if (debug) print("outputdir: + \'" + outputdir + "\'");
i++;
}
else if (String(arguments[i])=="-i"){
// process all files in an input directory
DirList.push(String(arguments[i+1]));
if (debug) print("inputdir: + \'" + arguments[i+1] + "\'");
i++;
}
else {
print("Unknown option: " + arguments[i] + "\n");
PrintOptions();
}
}
else
{
// we have a single file
if (debug) print("file: + \'" + arguments[i] + "\'");
FileList.push(String(arguments[i]));
}
}
// first handle the single files
for (var i in FileList)
processJSFile(FileList[i],null);
// then handle the input directories
for (var j in DirList) {
var inputdir = String(DirList[j]);
print("Process input directory: " + inputdir);
// clean up index arrays
var indexFileArray = [];
var indexFunctionArray = [];
// for the directory name get rid of ../../ or ..\..\
inputDirName = inputdir.replace(/\.\.\/|\.\.\\/g,"");
indexFile = indexFileName + "_" + inputDirName + ".html";
indexFunction = indexFunctionName + "_" + inputDirName + ".html";
print("indexFile = " + indexFile);
print("indexFunction = " + indexFunction);
// read the files in the directory
var DirFile = new java.io.File(inputdir);
var lst = DirFile.list();
var separator = Packages.java.io.File.separator;
for (var i=0; i < lst.length; i++)
{
processJSFile(String(lst[i]),inputdir);
}
// generate the index files for the input directory
GenerateIndex(inputDirName);
}

View File

@@ -1,19 +0,0 @@
/**
* liveConnect.js: a simple demonstration of JavaScript-to-Java connectivity
*/
// Create a new StringBuffer. Note that the class name must be fully qualified
// by its package. Packages other than "java" must start with "Packages", i.e.,
// "Packages.javax.servlet...".
var sb = new java.lang.StringBuffer();
// Now add some stuff to the buffer.
sb.append("hi, mom");
sb.append(3); // this will add "3.0" to the buffer since all JS numbers
// are doubles by default
sb.append(true);
// Now print it out. (The toString() method of sb is automatically called
// to convert the buffer to a string.)
// Should print "hi, mom3.0true".
print(sb);

View File

@@ -1,18 +0,0 @@
// unique.js: read the contents of a file and print out the unique lines
defineClass("File")
// "arguments[0]" refers to the first argument at the command line to the
// script, if present. If not present, "arguments[0]" will be undefined,
// which will cause f to read from System.in.
var f = new File(arguments[0]);
var o = {}
var line;
while ((line = f.readLine()) != null) {
// Use JavaScript objects' inherent nature as an associative
// array to provide uniqueness
o[line] = true;
}
for (i in o) {
print(i);
}

Binary file not shown.

View File

@@ -1,2 +0,0 @@
Manifest-Version: 1.0
Main-Class: org.mozilla.javascript.tools.shell.Main

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,112 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
package org.mozilla.javascript;
/**
* This class implements the "arguments" object.
*
* See ECMA 10.1.8
*
* @see org.mozilla.javascript.NativeCall
* @author Norris Boyd
*/
class Arguments extends ScriptableObject {
public Arguments(NativeCall activation) {
this.activation = activation;
Scriptable parent = activation.getParentScope();
setParentScope(parent);
setPrototype(ScriptableObject.getObjectPrototype(parent));
args = activation.getOriginalArguments();
int length = args.length;
Object callee = activation.funObj;
defineProperty("length", new Integer(length),
ScriptableObject.DONTENUM);
defineProperty("callee", callee, ScriptableObject.DONTENUM);
hasCaller = activation.funObj.version <= Context.VERSION_1_3;
}
public String getClassName() {
return "Arguments";
}
public boolean has(String name, Scriptable start) {
return (hasCaller && name.equals("caller")) || super.has(name, start);
}
public boolean has(int index, Scriptable start) {
Object[] args = activation.getOriginalArguments();
return (0 <= index && index < args.length) || super.has(index, start);
}
public Object get(String name, Scriptable start) {
if (hasCaller && name.equals("caller")) {
NativeCall caller = activation.caller;
if (caller == null || caller.originalArgs == null)
return null;
return caller.get("arguments", caller);
}
return super.get(name, start);
}
public Object get(int index, Scriptable start) {
if (0 <= index && index < args.length) {
NativeFunction f = activation.funObj;
if (index < f.argCount)
return activation.get(f.names[index+1], activation);
return args[index];
}
return super.get(index, start);
}
public void put(String name, Scriptable start, Object value) {
if (name.equals("caller")) {
// Set "hasCaller" to false so that we won't look up a
// computed value.
hasCaller = false;
}
super.put(name, start, value);
}
public void put(int index, Scriptable start, Object value) {
if (0 <= index && index < args.length) {
NativeFunction f = activation.funObj;
if (index < f.argCount)
activation.put(f.names[index+1], activation, value);
else
args[index] = value;
return;
}
super.put(index, start, value);
}
public void delete(String name) {
if (name.equals("caller"))
hasCaller = false;
super.delete(name);
}
private NativeCall activation;
private Object[] args;
private boolean hasCaller;
}

View File

@@ -1,58 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
package org.mozilla.javascript;
final class BinaryDigitReader {
int lgBase; // Logarithm of base of number
int digit; // Current digit value in radix given by base
int digitPos; // Bit position of last bit extracted from digit
String digits; // String containing the digits
int start; // Index of the first remaining digit
int end; // Index past the last remaining digit
BinaryDigitReader(int base, String digits, int start, int end) {
lgBase = 0;
while (base != 1) {
lgBase++;
base >>= 1;
}
digitPos = 0;
this.digits = digits;
this.start = start;
this.end = end;
}
/* Return the next binary digit from the number or -1 if done */
int getNextBinaryDigit()
{
if (digitPos == 0) {
if (start == end)
return -1;
char c = digits.charAt(start++);
if ('0' <= c && c <= '9')
digit = c - '0';
else if ('a' <= c && c <= 'z')
digit = c - 'a' + 10;
else digit = c - 'A' + 10;
digitPos = lgBase;
}
return digit >> --digitPos & 1;
}
}

View File

@@ -1,38 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
package org.mozilla.javascript;
public interface ClassNameHelper {
public String getTargetClassFileName();
public void setTargetClassFileName(String classFileName);
public String getTargetPackage();
public void setTargetPackage(String targetPackage);
public String getTargetClassFileName(String className);
public String getGeneratingDirectory();
public void setTargetExtends(Class extendsClass);
public void setTargetImplements(Class[] implementsClasses);
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,75 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
package org.mozilla.javascript;
/**
* This interface supports low-level per statement debug hooks
* <p>
* This interface can be implemented and used to do statement level
* interrupting and trapping of executing scripts.
* org.mozilla.javascript.debug.IDebugManager uses this system and provides
* a higher level abstraction more appropriate as a debugger API.
*
* @see org.mozilla.javascript.Context#setBytecodeHook
* @see org.mozilla.javascript.debug.IDebugManager
* @author John Bandhauer
*/
public interface DeepBytecodeHook
{
/**
* Possible hook return values.
*/
/**
* The executing script should continue.
*/
public static final int CONTINUE = 0;
/**
* The executing script should throw the object passed back in
* retVal[0]. This must be an object derived from java.lang.Throwable.
*/
public static final int THROW = 1;
/**
* The executing script should immediately return the object passed
* back in retVal[0].
*/
public static final int RETURN_VALUE = 2;
/**
* Handle trap.
*
* @param cx current context
* @param pc current program counter
* @param retVal single Object array to hold return value or exception
* @return one of {CONTINUE | THROW | RETURN_VALUE}
*/
public int trapped(Context cx, int pc, Object[] retVal);
/**
* Handle interrupt.
*
* @param cx current context
* @param pc current program counter
* @param retVal single Object array to hold return value or exception
* @return one of {CONTINUE | THROW | RETURN_VALUE}
*/
public int interrupted(Context cx, int pc, Object[] retVal);
}

View File

@@ -1,59 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
package org.mozilla.javascript;
/**
* This interface supports low-level call debug hooks
* <p>
* This interface can be implemented and used to hook JavaScript
* function calls. org.mozilla.javascript.debug.IDebugManager uses this
* system and provides a higher level abstraction more appropriate
* as a debugger API.
*
* @see org.mozilla.javascript.Context#setCallHook
* @see org.mozilla.javascript.debug.IDebugManager
* @author John Bandhauer
*/
public interface DeepCallHook
{
/**
* Notification that a function is about to be called.
*
* @param cx current context
* @param fun function to be called
* @param thisArg the 'this' of the object
* @param args constructor arguments array
* @return arbitrary object which is subsequently passed to postCall
* @see org.mozilla.javascript.debug.NativeDelegate#callDebug
*/
public Object preCall(Context cx, Object fun, Object thisArg, Object[] args);
/**
* Notification that a call returned.
*
* @param cx current context
* @param ref value returned from previous call to preCall
* @param retVal value returned by the callee (null if exception thrown)
* @param e exception thrown by callee (null if none thrown)
* @see org.mozilla.javascript.debug.NativeDelegate#callDebug
*/
public void postCall(Context cx, Object ref, Object retVal,
JavaScriptException e);
}

View File

@@ -1,53 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
package org.mozilla.javascript;
/**
* This interface supports low-level error reporter hooks.
* <p>
* This interface can be implemented and used to hook calls to the
* error reporter. org.mozilla.javascript.debug.IDebugManager uses this
* system and provides a higher level abstraction more appropriate
* as a debugger API.
*
* @see org.mozilla.javascript.Context#setErrorReporterHook
* @see org.mozilla.javascript.debug.IDebugManager
* @author John Bandhauer
*/
public interface DeepErrorReporterHook extends ErrorReporter
{
/**
* Change the current error reporter.
*
* @return the previous error reporter
* @see org.mozilla.javascript.ErrorReporter
* @see org.mozilla.javascript.Context#setErrorReporter
*/
public ErrorReporter setErrorReporter(ErrorReporter reporter);
/**
* Get the current error reporter.
*
* @see org.mozilla.javascript.ErrorReporter
* @see org.mozilla.javascript.Context#getErrorReporter
*/
public ErrorReporter getErrorReporter();
}

View File

@@ -1,58 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
package org.mozilla.javascript;
/**
* This interface supports low-level execute debug hooks.
* <p>
* This interface can be implemented and used to hook JavaScript
* script execution. org.mozilla.javascript.debug.IDebugManager uses this
* system and provides a higher level abstraction more appropriate
* as a debugger API.
*
* @see org.mozilla.javascript.Context#setExecuteHook
* @see org.mozilla.javascript.debug.IDebugManager
* @author John Bandhauer
*/
public interface DeepExecuteHook
{
/**
* Notification that a top-level script is about to be executed.
*
* @param cx current context
* @param fun script
* @param scope scope
* @return arbitrary object which is subsequently passed to postExecute
* @see org.mozilla.javascript.debug.NativeDelegate#executeDebug
*/
public Object preExecute(Context cx, NativeFunction fun, Scriptable scope);
/**
* Notification that a top-level script execution returned.
*
* @param cx current context
* @param ref value returned from previous call to preExecute
* @param retVal value returned by the script (null if exception thrown)
* @param e exception thrown by script (null if none thrown)
* @see org.mozilla.javascript.debug.NativeDelegate#executeDebug
*/
public void postExecute(Context cx, Object ref, Object retVal,
JavaScriptException e);
}

View File

@@ -1,59 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
package org.mozilla.javascript;
/**
* This interface supports low-level new object debug hooks.
* <p>
* This interface can be implemented and used to hook JavaScript
* object creation; i.e. calls for JavaScript constructor
* functions. org.mozilla.javascript.debug.IDebugManager uses this
* system and provides a higher level abstraction more appropriate
* as a debugger API.
*
* @see org.mozilla.javascript.Context#setNewObjectHook
* @see org.mozilla.javascript.debug.IDebugManager
* @author John Bandhauer
*/
public interface DeepNewObjectHook
{
/**
* Notification that a constructor is about to be called.
*
* @param cx current context
* @param fun constructor function
* @param args constructor arguments array
* @return arbitrary object which is subsequently passed to postNewObject
* @see org.mozilla.javascript.debug.NativeDelegate#newObjectDebug
*/
public Object preNewObject(Context cx, Object fun, Object[] args);
/**
* Notification that a constructor returned.
*
* @param cx current context
* @param ref value returned from previous call to preNewObject
* @param retVal value returned by the constructor (null if exception thrown)
* @param e exception thrown by constructor (null if none thrown)
* @see org.mozilla.javascript.debug.NativeDelegate#newObjectDebug
*/
public void postNewObject(Context cx, Object ref, Scriptable retVal,
Exception e);
}

View File

@@ -1,57 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
package org.mozilla.javascript;
/**
* This interface supports low-level script load/unload debug hooks.
* <p>
* This interface can be implemented and used to hook script loading
* and unloading. org.mozilla.javascript.debug.IDebugManager uses this
* system and provides a higher level abstraction more appropriate
* as a debugger API.
*
* @see org.mozilla.javascript.Context#setScriptHook
* @see org.mozilla.javascript.debug.IDebugManager
* @author John Bandhauer
*/
public interface DeepScriptHook
{
/**
* Notification that a script is being loaded.
*
* @param cx current context
* @param obj script or function being loaded
*/
public void loadingScript(Context cx, NativeFunction obj);
/**
* Notification that a script is being unloaded.
* <p>
* NOTE: this currently happens as part of the Java garbage
* collection process; i.e. it is called by the finalize method
* of the script and is thus likely to be called on a system
* thread. Also, this is thus not likely to be called if there are
* any outstanding references to the script.
*
* @param cx context which originally loaded the script
* @param obj script or function being unloaded
*/
public void unloadingScript(Context cx, NativeFunction obj);
}

View File

@@ -1,46 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
package org.mozilla.javascript;
/**
* This is the default error reporter for JavaScript.
*
* @author Norris Boyd
*/
class DefaultErrorReporter implements ErrorReporter {
public void warning(String message, String sourceName, int line,
String lineSource, int lineOffset)
{
// do nothing
}
public void error(String message, String sourceName, int line,
String lineSource, int lineOffset)
{
throw new EvaluatorException(message);
}
public EvaluatorException runtimeError(String message, String sourceName,
int line, String lineSource,
int lineOffset)
{
return new EvaluatorException(message);
}
}

View File

@@ -1,86 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
// API class
package org.mozilla.javascript;
/**
* This is interface defines a protocol for the reporting of
* errors during JavaScript translation or execution.
*
* @author Norris Boyd
*/
public interface ErrorReporter {
/**
* Report a warning.
*
* The implementing class may choose to ignore the warning
* if it desires.
*
* @param message a String describing the warning
* @param sourceName a String describing the JavaScript source
* where the warning occured; typically a filename or URL
* @param line the line number associated with the warning
* @param lineSource the text of the line (may be null)
* @param lineOffset the offset into lineSource where problem was detected
*/
void warning(String message, String sourceName, int line,
String lineSource, int lineOffset);
/**
* Report an error.
*
* The implementing class is free to throw an exception if
* it desires.
*
* If execution has not yet begun, the JavaScript engine is
* free to find additional errors rather than terminating
* the translation. It will not execute a script that had
* errors, however.
*
* @param message a String describing the error
* @param sourceName a String describing the JavaScript source
* where the error occured; typically a filename or URL
* @param line the line number associated with the error
* @param lineSource the text of the line (may be null)
* @param lineOffset the offset into lineSource where problem was detected
*/
void error(String message, String sourceName, int line,
String lineSource, int lineOffset);
/**
* Creates an EvaluatorException that may be thrown.
*
* runtimeErrors, unlike errors, will always terminate the
* current script.
*
* @param message a String describing the error
* @param sourceName a String describing the JavaScript source
* where the error occured; typically a filename or URL
* @param line the line number associated with the error
* @param lineSource the text of the line (may be null)
* @param lineOffset the offset into lineSource where problem was detected
* @return an EvaluatorException that will be thrown.
*/
EvaluatorException runtimeError(String message, String sourceName,
int line, String lineSource,
int lineOffset);
}

View File

@@ -1,38 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
package org.mozilla.javascript;
/**
* The class of exceptions thrown by the JavaScript engine.
*/
public class EvaluatorException extends RuntimeException {
/**
* Create an exception with the specified detail message.
*
* Errors internal to the JavaScript engine will simply throw a
* RuntimeException.
*
* @param detail a message with detail about the exception
*/
public EvaluatorException(String detail) {
super(detail);
}
}

View File

@@ -1,322 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
// API class
package org.mozilla.javascript;
import java.util.Hashtable;
import java.util.Enumeration;
/**
* Manipulate a Scriptable object as if its prototype chain were flattened.
* <p>
* Compared to the Scriptable interface, FlattenedObject provides a view of
* Scriptable objects that is easier to use and more closely matches a script
* writer's view of a JavaScript object. <p>
*
* A FlattenedObject is "flattened" in the sense that multiple objects in a
* prototype chain appear to have their properties all appear in the
* FlattenedObject. <p>
*
* Another convenience provided by Flattened object is the ability to access
* properties by a single java.lang.Object id. This id is then converted into
* a String or an int before methods of the Scriptable object are called.
*
* @see org.mozilla.javascript.Scriptable
*
* @author Norris Boyd
*/
public class FlattenedObject {
/**
* Construct a new FlattenedObject.
*
* @param object the object to be viewed with flattened properties
*/
public FlattenedObject(Scriptable object) {
this.obj = object;
}
/**
* Get the associated Scriptable object.
*/
public Scriptable getObject() {
return obj;
}
/**
* Determine if a property exists in an object.
*
* This is a more convenient (and less efficient) form than
* <code>Scriptable.has()</code>.
* It returns true if and only if the property
* exists in this object or any of the objects in its prototype
* chain.
*
* @param id the property index, which may be either a String or a
* Number
* @return true if and only if the property exists in the prototype
* chain
* @see org.mozilla.javascript.Scriptable#has
*/
public boolean hasProperty(Object id) {
String stringId = ScriptRuntime.toString(id);
String s = ScriptRuntime.getStringId(stringId);
if (s == null)
return getBase(obj, ScriptRuntime.getIntId(stringId)) != null;
return getBase(obj, s) != null;
}
/**
* Get a property of an object.
* <p>
* This is a more convenient (and less efficient) form than
* <code>Scriptable.get()</code>. It corresponds exactly to the
* expression <code>obj[id]</code> in JavaScript. This method
* will traverse the prototype chain of an object to find the
* property.<p>
*
* If the property does not exist in the object or its prototype
* chain, the undefined value will be returned.
*
* @param id the property index; can be a String or a Number; the
* String may contain characters representing a number
* @return the value of the property or the undefined value
* @see org.mozilla.javascript.Scriptable#get
* @see org.mozilla.javascript.Context#getUndefinedValue
*/
public Object getProperty(Object id) {
String s = ScriptRuntime.getStringId(id);
int index = s == null ? ScriptRuntime.getIntId(id) : 0;
Scriptable m = obj;
Object result;
for(;;) {
result = s == null ? m.get(index, obj) : m.get(s, obj);
if (result != Scriptable.NOT_FOUND)
break;
m = m.getPrototype();
if (m == null)
return Undefined.instance;
}
if (result instanceof Scriptable)
return new FlattenedObject((Scriptable) result);
return result;
}
/**
* Set a property of an object.
*
* This is a more convenient (and less efficient) form than that
* provided in Scriptable. It corresponds exactly to the
* expression <code>obj[id] = val</code> in JavaScript.<p>
*
* @param id the property index, which may be either a String or
* a Number
* @param value the value of the property
* @see org.mozilla.javascript.Scriptable#put
*/
public void putProperty(Object id, Object value) {
String s = ScriptRuntime.getStringId(id);
if (value instanceof FlattenedObject)
value = ((FlattenedObject) value).getObject();
Scriptable x;
if (s == null) {
int index = ScriptRuntime.getIntId(id);
x = getBase(obj, index);
if (x == null)
x = obj;
x.put(index, obj, value);
return;
}
x = getBase(obj, s);
if (x == null)
x = obj;
x.put(s, obj, value);
}
/**
* Remove a property.
*
* This method provides the functionality of the <code>delete</code>
* operator in JavaScript.
*
* @param id the property index, which may be either a String or
* a Number
* @return true if the property didn't exist, or existed and was removed
* @see org.mozilla.javascript.Scriptable#delete
*/
public boolean deleteProperty(Object id) {
String s = ScriptRuntime.getStringId(id);
if (s == null) {
int index = ScriptRuntime.getIntId(id);
Scriptable base = getBase(obj, index);
if (base == null)
return true;
base.delete(index);
return !base.has(index, base);
}
Scriptable base = getBase(obj, s);
if (base == null)
return true;
base.delete(s);
return !base.has(s, base);
}
/**
* Return an array that contains the ids of the properties.
*
* <p>This method will walk the prototype chain and collect the
* ids of all objects in the prototype chain.<p>
*
* If an id appears in more than one object in the prototype chain,
* it will only be in the array once. (So all the entries in the
* array will be unique respective to equals().)
*
* @see org.mozilla.javascript.Scriptable#getIds
*/
public Object[] getIds() {
Hashtable h = new Hashtable(11);
Scriptable m = obj;
while (m != null) {
Object[] e = m.getIds();
for (int i=0; i < e.length; i++) {
h.put(e[i], Boolean.TRUE);
}
m = m.getPrototype();
}
Enumeration keys = h.keys();
Object elem;
Object[] result = new Object[h.size()];
int index = 0;
while (keys.hasMoreElements()) {
elem = keys.nextElement();
result[index++] = elem;
}
return result;
}
/**
* Consider this object to be a function, and call it.
*
* @param cx the current Context for this thread
* @param thisObj the JavaScript 'this' for the call
* @param args the arguments for the call
* @return the result of the JavaScript function call
* @exception NotAFunctionException if this object is not a function
* @exception JavaScriptException if an uncaught JavaScript exception
* occurred while executing the function
* @see org.mozilla.javascript.Function#call
*/
public Object call(Context cx, Scriptable thisObj, Object[] args)
throws NotAFunctionException,
JavaScriptException
{
if (!(obj instanceof Function)) {
throw new NotAFunctionException();
}
return ScriptRuntime.call(cx, obj, thisObj, args);
}
/**
* Consider this object to be a function, and invoke it as a
* constructor call.
*
* @param cx the current Context for this thread
* @param args the arguments for the constructor call
* @return the allocated object
* @exception NotAFunctionException if this object is not a function
* @exception JavaScriptException if an uncaught JavaScript exception
* occurred while executing the constructor
* @see org.mozilla.javascript.Function#construct
*/
public Scriptable construct(Context cx, Object[] args)
throws NotAFunctionException,
JavaScriptException
{
if (!(obj instanceof Function)) {
throw new NotAFunctionException();
}
return ScriptRuntime.newObject(cx, obj, args, null);
}
/**
* Get the property indicated by the id, and invoke it with the
* specified arguments.
* <p>
* For example, for a FlattenedObject <code>obj</code>,
* and a Java array <code>a</code> consisting of a single string
* <code>"hi"</code>, the call <pre>
* obj.callMethod("m", a)</pre>
* is equivalent to the JavaScript code <code>obj.m("hi")</code>.<p>
*
* If the property is not found or is not a function, an
* exception will be thrown.
*
* @param id the Number or String to use to find the function property
* to call
* @param args the arguments for the constructor call
* @return the result of the call
* @exception PropertyException if the designated property
* was not found
* @exception NotAFunctionException if this object is not a function
* @exception JavaScriptException if an uncaught JavaScript exception
* occurred while executing the method
* @see org.mozilla.javascript.Function#call
*/
public Object callMethod(Object id, Object[] args)
throws PropertyException,
NotAFunctionException,
JavaScriptException
{
if (!hasProperty(id)) {
throw new PropertyException(
Context.getMessage("msg.prop.not.found", null));
}
Object o = getProperty(id);
if (o instanceof FlattenedObject)
return ((FlattenedObject) o).call(Context.getContext(), obj, args);
throw new NotAFunctionException();
}
/****** End of API *******/
private static Scriptable getBase(Scriptable obj, String s) {
Scriptable m = obj;
while (m != null) {
if (m.has(s, obj))
return m;
m = m.getPrototype();
}
return null;
}
private static Scriptable getBase(Scriptable obj, int index) {
Scriptable m = obj;
while (m != null) {
if (m.has(index, obj))
return m;
m = m.getPrototype();
}
return null;
}
private Scriptable obj;
}

View File

@@ -1,69 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
// API class
package org.mozilla.javascript;
/**
* This is interface that all functions in JavaScript must implement.
* The interface provides for calling functions and constructors.
*
* @see org.mozilla.javascript.Scriptable
* @author Norris Boyd
*/
public interface Function extends Scriptable {
/**
* Call the function.
*
* Note that the array of arguments is not guaranteed to have
* length greater than 0.
*
* @param cx the current Context for this thread
* @param scope the scope to execute the function relative to. This
* set to the value returned by getParentScope() except
* when the function is called from a closure.
* @param thisObj the JavaScript <code>this</code> object
* @param args the array of arguments
* @return the result of the call
* @exception JavaScriptException if an uncaught exception
* occurred while executing the function
*/
public Object call(Context cx, Scriptable scope, Scriptable thisObj,
Object[] args)
throws JavaScriptException;
/**
* Call the function as a constructor.
*
* This method is invoked by the runtime in order to satisfy a use
* of the JavaScript <code>new</code> operator. This method is
* expected to create a new object and return it.
*
* @param cx the current Context for this thread
* @param scope an enclosing scope of the caller except
* when the function is called from a closure.
* @param args the array of arguments
* @return the allocated object
* @exception JavaScriptException if an uncaught exception
* occurred while executing the constructor
*/
public Scriptable construct(Context cx, Scriptable scope, Object[] args)
throws JavaScriptException;
}

View File

@@ -1,48 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
package org.mozilla.javascript;
import java.util.*;
public class FunctionNode extends Node {
public FunctionNode(String name, Node left, Node right) {
super(TokenStream.FUNCTION, left, right, name);
itsVariableTable = new VariableTable();
}
public String getFunctionName() {
return getString();
}
public VariableTable getVariableTable() {
return itsVariableTable;
}
public boolean requiresActivation() {
return itsNeedsActivation;
}
public boolean setRequiresActivation(boolean b) {
return itsNeedsActivation = b;
}
protected VariableTable itsVariableTable;
protected boolean itsNeedsActivation;
}

View File

@@ -1,527 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
// API class
package org.mozilla.javascript;
import java.util.Hashtable;
import java.util.Vector;
import java.lang.reflect.*;
public class FunctionObject extends NativeFunction {
/**
* Create a JavaScript function object from a Java method.
*
* <p>The <code>member</code> argument must be either a java.lang.reflect.Method
* or a java.lang.reflect.Constructor and must match one of two forms.<p>
*
* The first form is a member with zero or more parameters
* of the following types: Object, String, boolean, Scriptable,
* byte, short, int, long, float, or double. If the member is
* a Method, the return value must be void or one of the types
* allowed for parameters.<p>
*
* The runtime will perform appropriate conversions based
* upon the type of the parameter. A parameter type of
* Object specifies that no conversions are to be done. A parameter
* of type String will use Context.toString to convert arguments.
* Similarly, parameters of type double, boolean, and Scriptable
* will cause Context.toNumber, Context.toBoolean, and
* Context.toObject, respectively, to be called.<p>
*
* If the method is not static, the Java 'this' value will
* correspond to the JavaScript 'this' value. Any attempt
* to call the function with a 'this' value that is not
* of the right Java type will result in an error.<p>
*
* The second form is the variable arguments (or "varargs")
* form. If the FunctionObject will be used as a constructor,
* the member must have the following parameters
* <pre>
* (Context cx, Object[] args, Function ctorObj,
* boolean inNewExpr)</pre>
* and if it is a Method, be static and return an Object result.<p>
*
* Otherwise, if the FunctionObject will <i>not</i> be used to define a
* constructor, the member must be a static Method with parameters
* (Context cx, Scriptable thisObj, Object[] args,
* Function funObj) </pre>
* <pre>
* and an Object result.<p>
*
* When the function varargs form is called as part of a function call,
* the <code>args</code> parameter contains the
* arguments, with <code>thisObj</code>
* set to the JavaScript 'this' value. <code>funObj</code>
* is the function object for the invoked function.<p>
*
* When the constructor varargs form is called or invoked while evaluating
* a <code>new</code> expression, <code>args</code> contains the
* arguments, <code>ctorObj</code> refers to this FunctionObject, and
* <code>inNewExpr</code> is true if and only if a <code>new</code>
* expression caused the call. This supports defining a function that
* has different behavior when called as a constructor than when
* invoked as a normal function call. (For example, the Boolean
* constructor, when called as a function,
* will convert to boolean rather than creating a new object.)<p>
*
* @param name the name of the function
* @param methodOrConstructor a java.lang.reflect.Method or a java.lang.reflect.Constructor
* that defines the object
* @param scope enclosing scope of function
* @see org.mozilla.javascript.Scriptable
*/
public FunctionObject(String name, Member methodOrConstructor,
Scriptable scope)
{
String methodName;
if (methodOrConstructor instanceof Constructor) {
ctor = (Constructor) methodOrConstructor;
isStatic = true; // well, doesn't take a 'this'
types = ctor.getParameterTypes();
methodName = ctor.getName();
} else {
method = (Method) methodOrConstructor;
isStatic = Modifier.isStatic(method.getModifiers());
types = method.getParameterTypes();
methodName = method.getName();
}
String myNames[] = { name };
super.names = myNames;
int length;
if (types.length == 4 && (types[1].isArray() || types[2].isArray())) {
// Either variable args or an error.
if (types[1].isArray()) {
if (!isStatic ||
types[0] != Context.class ||
types[1].getComponentType() != ScriptRuntime.ObjectClass ||
types[2] != ScriptRuntime.FunctionClass ||
types[3] != Boolean.TYPE)
{
String[] args = { methodName };
String message = Context.getMessage("msg.varargs.ctor",
args);
throw Context.reportRuntimeError(message);
}
parmsLength = VARARGS_CTOR;
} else {
if (!isStatic ||
types[0] != Context.class ||
types[1] != ScriptRuntime.ScriptableClass ||
types[2].getComponentType() != ScriptRuntime.ObjectClass ||
types[3] != ScriptRuntime.FunctionClass)
{
String[] args = { methodName };
String message = Context.getMessage("msg.varargs.fun",
args);
throw Context.reportRuntimeError(message);
}
parmsLength = VARARGS_METHOD;
}
// XXX check return type
length = 1;
} else {
parmsLength = (short) types.length;
boolean hasConversions = false;
for (int i=0; i < parmsLength; i++) {
Class type = types[i];
if (type == ScriptRuntime.ObjectClass) {
// may not need conversions
} else if (type == ScriptRuntime.StringClass ||
type == ScriptRuntime.BooleanClass ||
ScriptRuntime.NumberClass.isAssignableFrom(type) ||
Scriptable.class.isAssignableFrom(type))
{
hasConversions = true;
} else if (type == Boolean.TYPE) {
hasConversions = true;
types[i] = ScriptRuntime.BooleanClass;
} else if (type == Byte.TYPE) {
hasConversions = true;
types[i] = ScriptRuntime.ByteClass;
} else if (type == Short.TYPE) {
hasConversions = true;
types[i] = ScriptRuntime.ShortClass;
} else if (type == Integer.TYPE) {
hasConversions = true;
types[i] = ScriptRuntime.IntegerClass;
} else if (type == Float.TYPE) {
hasConversions = true;
types[i] = ScriptRuntime.FloatClass;
} else if (type == Double.TYPE) {
hasConversions = true;
types[i] = ScriptRuntime.DoubleClass;
} else {
Object[] errArgs = { methodName };
throw Context.reportRuntimeError(
Context.getMessage("msg.bad.parms", errArgs));
}
}
if (!hasConversions)
types = null;
length = parmsLength;
}
// Initialize length property
lengthPropertyValue = (short) length;
hasVoidReturn = method != null && method.getReturnType() == Void.TYPE;
this.argCount = (short) length;
setParentScope(scope);
setPrototype(getFunctionPrototype(scope));
}
/**
* Override ScriptableObject's has, get, and set in order to define
* the "length" property of the function. <p>
*
* We could also have defined the property using ScriptableObject's
* defineProperty method, but that would have consumed a slot in every
* FunctionObject. Most FunctionObjects typically don't have any
* properties anyway, so having the "length" property would cause us
* to allocate an array of slots. <p>
*
* In particular, this method will return true for
* <code>name.equals("length")</code>
* and will delegate to the superclass for all other
* values of <code>name</code>.
*/
public boolean has(String name, Scriptable start) {
return name.equals("length") || super.has(name, start);
}
/**
* Override ScriptableObject's has, get, and set in order to define
* the "length" property of the function. <p>
*
* In particular, this method will return the value defined by
* the method used to construct the object (number of parameters
* of the method, or 1 if the method is a "varargs" form), unless
* setLength has been called with a new value.
*
* @see org.mozilla.javascript.FunctionObject#setLength
*/
public Object get(String name, Scriptable start) {
if (name.equals("length"))
return new Integer(lengthPropertyValue);
return super.get(name, start);
}
/**
* Override ScriptableObject's has, get, and set in order to define
* the "length" property of the function. <p>
*
* In particular, this method will ignore all attempts to set the
* "length" property and forward all other requests to ScriptableObject.
*
* @see org.mozilla.javascript.FunctionObject#setLength
*/
public void put(String name, Scriptable start, Object value) {
if (!name.equals("length"))
super.put(name, start, value);
}
/**
* Set the value of the "length" property.
*
* <p>Changing the value of the "length" property of a FunctionObject only
* affects the value retrieved from get() and does not affect the way
* the method itself is called. <p>
*
* The "length" property will be defined by default as the number
* of parameters of the method used to construct the FunctionObject,
* unless the method is a "varargs" form, in which case the "length"
* property will be defined to 1.
*
* @param length the new length
*/
public void setLength(short length) {
lengthPropertyValue = length;
}
// TODO: Make not public
/**
* Finds methods of a given name in a given class.
*
* <p>Searches <code>clazz</code> for methods with name
* <code>name</code>. Maintains a cache so that multiple
* lookups on the same class are cheap.
*
* @param clazz the class to search
* @param name the name of the methods to find
* @return an array of the found methods, or null if no methods
* by that name were found.
* @see java.lang.Class#getMethods
*/
public static Method[] findMethods(Class clazz, String name) {
Vector v = new Vector(5);
Method[] methods = clazz.getMethods();
for (int i=0; i < methods.length; i++) {
if (methods[i].getDeclaringClass() == clazz &&
methods[i].getName().equals(name)){
v.addElement(methods[i]);
}
}
if (v.size() == 0) {
return null;
}
Method[] result = new Method[v.size()];
v.copyInto(result);
return result;
}
/**
* Define this function as a JavaScript constructor.
* <p>
* Sets up the "prototype" and "constructor" properties. Also
* calls setParent and setPrototype with appropriate values.
* Then adds the function object as a property of the given scope, using
* <code>prototype.getClassName()</code>
* as the name of the property.
*
* @param scope the scope in which to define the constructor (typically
* the global object)
* @param prototype the prototype object
* @see org.mozilla.javascript.Scriptable#setParentScope
* @see org.mozilla.javascript.Scriptable#setPrototype
* @see org.mozilla.javascript.Scriptable#getClassName
*/
public void addAsConstructor(Scriptable scope, Scriptable prototype) {
setParentScope(scope);
setPrototype(getFunctionPrototype(scope));
prototype.setParentScope(this);
final int attr = ScriptableObject.DONTENUM |
ScriptableObject.PERMANENT |
ScriptableObject.READONLY;
defineProperty("prototype", prototype, attr);
String name = prototype.getClassName();
if (!name.equals("With")) {
// A "With" object would delegate these calls to the prototype:
// not the right thing to do here!
if (prototype instanceof ScriptableObject) {
((ScriptableObject) prototype).defineProperty("constructor",
this, attr);
} else {
prototype.put("constructor", prototype, this);
}
}
if (scope instanceof ScriptableObject) {
((ScriptableObject) scope).defineProperty(name, this,
ScriptableObject.DONTENUM);
} else {
scope.put(name, scope, this);
}
setParentScope(scope);
}
static public Object convertArg(Scriptable scope,
Object arg, Class desired)
{
if (desired == ScriptRuntime.BooleanClass
|| desired == Boolean.TYPE)
return ScriptRuntime.toBoolean(arg) ? Boolean.TRUE
: Boolean.FALSE;
else if (desired == ScriptRuntime.StringClass)
return ScriptRuntime.toString(arg);
else if (desired == ScriptRuntime.IntegerClass
|| desired == Integer.TYPE)
return new Integer(ScriptRuntime.toInt32(arg));
else if (desired == ScriptRuntime.DoubleClass
|| desired == Double.TYPE)
return new Double(ScriptRuntime.toNumber(arg));
else if (desired == ScriptRuntime.ScriptableClass)
return ScriptRuntime.toObject(scope, arg);
else {
Object[] errArgs = { desired.getName() };
throw Context.reportRuntimeError(
Context.getMessage("msg.cant.convert", errArgs));
}
}
/**
* Performs conversions on argument types if needed and
* invokes the underlying Java method or constructor.
* <p>
* Implements Function.call.
*
* @see org.mozilla.javascript.Function#call
* @exception JavaScriptException if the underlying Java method or constructor
* threw an exception
*/
public Object call(Context cx, Scriptable scope, Scriptable thisObj,
Object[] args)
throws JavaScriptException
{
if (parmsLength < 0)
return callVarargs(cx, thisObj, args, false);
if (!isStatic) {
// OPT: cache "clazz"?
Class clazz = method != null ? method.getDeclaringClass()
: ctor.getDeclaringClass();
Scriptable p = thisObj;
while (p != null && !clazz.isInstance(p)) {
// Walk up the prototype chain to find an object to call the
// method on
p = p.getPrototype();
}
if (p == null) {
// Couldn't find an object to call this on.
Object[] errArgs = { names[0] };
throw Context.reportRuntimeError(
Context.getMessage("msg.incompat.call", errArgs));
}
thisObj = p;
}
Object[] invokeArgs;
int i;
if (parmsLength == args.length) {
invokeArgs = args;
// avoid copy loop if no conversions needed
i = (types == null) ? parmsLength : 0;
} else {
invokeArgs = new Object[parmsLength];
i = 0;
}
for (; i < parmsLength; i++) {
Object arg = (i < args.length)
? args[i]
: Undefined.instance;
if (types != null) {
arg = convertArg(this, arg, types[i]);
}
invokeArgs[i] = arg;
}
try {
Object result = (method != null)
? method.invoke(thisObj, invokeArgs)
: ctor.newInstance(invokeArgs);
return hasVoidReturn ? Undefined.instance : result;
}
catch (InvocationTargetException e) {
throw JavaScriptException.wrapException(scope, e);
}
catch (IllegalAccessException e) {
throw WrappedException.wrapException(e);
}
catch (InstantiationException e) {
throw WrappedException.wrapException(e);
}
}
/**
* Performs conversions on argument types if needed and
* invokes the underlying Java method or constructor
* to create a new Scriptable object.
* <p>
* Implements Function.construct.
*
* @param cx the current Context for this thread
* @param scope the scope to execute the function relative to. This
* set to the value returned by getParentScope() except
* when the function is called from a closure.
* @param args arguments to the constructor
* @see org.mozilla.javascript.Function#construct
* @exception JavaScriptException if the underlying Java method or constructor
* threw an exception
*/
public Scriptable construct(Context cx, Scriptable scope, Object[] args)
throws JavaScriptException
{
if (method == null || parmsLength == VARARGS_CTOR) {
Scriptable result;
if (method != null) {
// Ugly: allow variable-arg constructors that need access to the
// scope to get it from the Context. Cleanest solution would be
// to modify the varargs form, but that would require users with
// the old form to change their code.
cx.ctorScope = scope;
result = (Scriptable) callVarargs(cx, null, args, true);
cx.ctorScope = null;
} else {
result = (Scriptable) call(cx, scope, null, args);
}
if (result.getPrototype() == null)
result.setPrototype(getClassPrototype());
if (result.getParentScope() == null) {
Scriptable parent = getParentScope();
if (result != parent)
result.setParentScope(parent);
}
return result;
}
return super.construct(cx, scope, args);
}
private Object callVarargs(Context cx, Scriptable thisObj, Object[] args,
boolean inNewExpr)
throws JavaScriptException
{
try {
if (parmsLength == VARARGS_METHOD) {
Object[] invokeArgs = { cx, thisObj, args, this };
Object result = method.invoke(null, invokeArgs);
return hasVoidReturn ? Undefined.instance : result;
} else {
Boolean b = inNewExpr ? Boolean.TRUE : Boolean.FALSE;
Object[] invokeArgs = { cx, args, this, b };
return (method == null)
? ctor.newInstance(invokeArgs)
: method.invoke(null, invokeArgs);
}
}
catch (InvocationTargetException e) {
Throwable target = e.getTargetException();
if (target instanceof EvaluatorException)
throw (EvaluatorException) target;
Scriptable scope = thisObj == null ? this : thisObj;
throw JavaScriptException.wrapException(scope, target);
}
catch (IllegalAccessException e) {
throw WrappedException.wrapException(e);
}
catch (InstantiationException e) {
throw WrappedException.wrapException(e);
}
}
boolean isVarArgsMethod() {
return parmsLength == VARARGS_METHOD;
}
boolean isVarArgsConstructor() {
return parmsLength == VARARGS_CTOR;
}
private static final short VARARGS_METHOD = -1;
private static final short VARARGS_CTOR = -2;
Method method;
Constructor ctor;
private Class[] types;
private short parmsLength;
private short lengthPropertyValue;
private boolean hasVoidReturn;
private boolean isStatic;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,126 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
// API class
package org.mozilla.javascript;
import java.util.Vector;
/**
* Class ImporterTopLevel
*
* This class defines a ScriptableObject that can be instantiated
* as a top-level ("global") object to provide functionality similar
* to Java's "import" statement.
* <p>
* This class can be used to create a top-level scope using the following code:
* <pre>
* Scriptable scope = cx.initStandardObjects(new ImporterTopLevel());
* </pre>
* Then JavaScript code will have access to the following methods:
* <ul>
* <li>importClass - will "import" a class by making its unqualified name
* available as a property of the top-level scope
* <li>importPackage - will "import" all the classes of the package by
* searching for unqualified names as classes qualified
* by the given package.
* </ul>
* The following code from the shell illustrates this use:
* <pre>
* js> importClass(java.io.File)
* js> f = new File('help.txt')
* help.txt
* js> importPackage(java.util)
* js> v = new Vector()
* []
*
* @author Norris Boyd
*/
public class ImporterTopLevel extends ScriptableObject {
public ImporterTopLevel() {
String[] names = { "importClass", "importPackage" };
try {
this.defineFunctionProperties(names, ImporterTopLevel.class,
ScriptableObject.DONTENUM);
} catch (PropertyException e) {
throw new Error(); // should never happen
}
}
public String getClassName() {
return "global";
}
public Object get(String name, Scriptable start) {
Object result = super.get(name, start);
if (result == NOT_FOUND && importedPackages != null) {
for (int i=0; i < importedPackages.size(); i++) {
Object o = importedPackages.elementAt(i);
NativeJavaPackage p = (NativeJavaPackage) o;
Object v = p.getPkgProperty(name, start, false);
if (v != null && !(v instanceof NativeJavaPackage)) {
if (result == NOT_FOUND) {
result = v;
} else {
String[] args = { result.toString(), v.toString() };
throw Context.reportRuntimeError(
Context.getMessage("msg.ambig.import",
args));
}
}
}
}
return result;
}
public void importClass(Object cl) {
if (!(cl instanceof NativeJavaClass)) {
String[] args = { Context.toString(cl) };
throw Context.reportRuntimeError(
Context.getMessage("msg.not.class", args));
}
String s = ((NativeJavaClass) cl).getClassObject().getName();
String n = s.substring(s.lastIndexOf('.')+1);
if (this.has(n, this)) {
String[] args = { n };
throw Context.reportRuntimeError(
Context.getMessage("msg.prop.defined", args));
}
this.defineProperty(n, cl, DONTENUM);
}
public void importPackage(Object pkg) {
if (importedPackages == null)
importedPackages = new Vector();
if (!(pkg instanceof NativeJavaPackage)) {
String[] args = { Context.toString(pkg) };
throw Context.reportRuntimeError(
Context.getMessage("msg.not.pkg", args));
}
for (int i=0; i < importedPackages.size(); i++) {
if (pkg == importedPackages.elementAt(i))
return; // allready in list
}
importedPackages.addElement(pkg);
}
private Vector importedPackages;
}

View File

@@ -1,55 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
package org.mozilla.javascript;
class InterpretedFunction extends NativeFunction {
InterpretedFunction(InterpreterData theData, Context cx)
{
itsData = theData;
// probably too much copying going on from theData to the InterpretedFunction object
// should pass them as parameters - unless we need them in the data block anyway?
names = new String[itsData.itsVariableTable.size() + 1];
names[0] = itsData.itsName;
for (int i = 0; i < itsData.itsVariableTable.size(); i++)
names[i + 1] = itsData.itsVariableTable.getName(i);
argCount = (short)itsData.itsVariableTable.getParameterCount();
source = itsData.itsSource;
nestedFunctions = itsData.itsNestedFunctions;
version = (short)cx.getLanguageVersion();
}
public Object call(Context cx, Scriptable scope, Scriptable thisObj,
Object[] args)
throws JavaScriptException
{
if (itsData.itsNeedsActivation)
scope = ScriptRuntime.initVarObj(cx, scope, this, thisObj, args);
itsData.itsCX = cx;
itsData.itsScope = scope;
itsData.itsThisObj = thisObj;
itsData.itsInArgs = args;
return Interpreter.interpret(itsData);
}
InterpreterData itsData;
}

View File

@@ -1,55 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
package org.mozilla.javascript;
public class InterpretedScript extends NativeScript {
InterpretedScript(InterpreterData theData, Context cx)
{
itsData = theData;
names = new String[itsData.itsVariableTable.size() + 1];
names[0] = "";
for (int i = 0; i < itsData.itsVariableTable.size(); i++)
names[i + 1] = itsData.itsVariableTable.getName(i);
nestedFunctions = itsData.itsNestedFunctions;
version = (short)cx.getLanguageVersion();
}
public Object exec(Context cx, Scriptable scope)
throws JavaScriptException
{
return call(cx, scope, scope, null);
}
public Object call(Context cx, Scriptable scope,
Scriptable thisObj, Object[] args)
throws JavaScriptException
{
scope = ScriptRuntime.initScript(cx, scope, this, thisObj);
itsData.itsCX = cx;
itsData.itsScope = scope;
itsData.itsThisObj = thisObj;
itsData.itsInArgs = args;
return Interpreter.interpret(itsData);
}
InterpreterData itsData;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,80 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
package org.mozilla.javascript;
import java.util.Vector;
class InterpreterData {
static final int INITIAL_MAX_ICODE_LENGTH = 1024;
static final int INITIAL_STRINGTABLE_SIZE = 64;
static final int INITIAL_NUMBERTABLE_SIZE = 64;
InterpreterData(int lastICodeTop, int lastStringTableIndex,
int lastNumberTableIndex, Object securityDomain)
{
itsICodeTop = lastICodeTop == 0
? INITIAL_MAX_ICODE_LENGTH
: lastICodeTop * 2;
itsICode = new byte[itsICodeTop];
itsStringTable = new String[lastStringTableIndex == 0
? INITIAL_STRINGTABLE_SIZE
: lastStringTableIndex * 2];
itsNumberTable = new Number[lastNumberTableIndex == 0
? INITIAL_NUMBERTABLE_SIZE
: lastNumberTableIndex * 2];
if (securityDomain == null && Context.isSecurityDomainRequired())
throw new SecurityException("Required security context missing");
this.securityDomain = securityDomain;
}
VariableTable itsVariableTable;
String itsName;
String itsSource;
boolean itsNeedsActivation;
String[] itsStringTable;
int itsStringTableIndex;
Number[] itsNumberTable;
int itsNumberTableIndex;
InterpretedFunction[] itsNestedFunctions;
Object[] itsRegExpLiterals;
byte[] itsICode;
int itsICodeTop;
int itsMaxLocals;
int itsMaxArgs;
int itsMaxStack;
int itsMaxTryDepth;
Object securityDomain;
Context itsCX;
Scriptable itsScope;
Scriptable itsThisObj;
Object[] itsInArgs;
}

View File

@@ -1,648 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
package org.mozilla.javascript;
import org.mozilla.classfile.*;
import java.lang.reflect.*;
import java.io.*;
import java.util.*;
public class JavaAdapter extends ScriptableObject {
public String getClassName() {
return "JavaAdapter";
}
public static Object js_JavaAdapter(Context cx, Object[] args,
Function ctorObj, boolean inNewExpr)
throws InstantiationException, NoSuchMethodException,
IllegalAccessException, InvocationTargetException,
ClassNotFoundException
{
Class superClass = Object.class;
Class[] intfs = new Class[args.length-1];
int interfaceCount = 0;
for (int i=0; i < args.length-1; i++) {
if (!(args[i] instanceof NativeJavaClass)) {
// TODO: report error
throw new RuntimeException("expected java class object");
}
Class c = ((NativeJavaClass) args[i]).getClassObject();
if (!c.isInterface()) {
superClass = c;
break;
}
intfs[interfaceCount++] = c;
}
StringBuffer sb = new StringBuffer("adapter");
sb.append(serial++);
Class[] interfaces = new Class[interfaceCount];
System.arraycopy(intfs, 0, interfaces, 0, interfaceCount);
Scriptable obj = (Scriptable) args[args.length - 1];
Class adapterClass = createAdapterClass(cx, obj, sb.toString(),
superClass, interfaces,
null, null);
Class[] ctorParms = { FlattenedObject.class };
Object[] ctorArgs = { new FlattenedObject(obj) };
Object v = adapterClass.getConstructor(ctorParms).newInstance(ctorArgs);
return cx.toObject(v, ScriptableObject.getTopLevelScope(ctorObj));
}
public static Class createAdapterClass(Context cx, Scriptable jsObj,
String name, Class superClass,
Class[] interfaces,
String scriptClassName,
ClassNameHelper nameHelper)
throws ClassNotFoundException
{
ClassFileWriter cfw = new ClassFileWriter(name,
superClass.getName(),
"<adapter>");
cfw.addField("o", "Lorg/mozilla/javascript/FlattenedObject;",
ClassFileWriter.ACC_PRIVATE);
cfw.addField("self", "Lorg/mozilla/javascript/Scriptable;",
(short) (ClassFileWriter.ACC_PUBLIC |
ClassFileWriter.ACC_FINAL));
int interfacesCount = interfaces == null ? 0 : interfaces.length;
for (int i=0; i < interfacesCount; i++) {
if (interfaces[i] != null)
cfw.addInterface(interfaces[i].getName());
}
generateCtor(cfw, name, superClass);
if (scriptClassName != null)
generateEmptyCtor(cfw, name, superClass, scriptClassName);
Hashtable generatedOverrides = new Hashtable();
// if abstract class was specified, then generate appropriate overrides.
for (int i = 0; i < interfacesCount; i++) {
Method[] methods = interfaces[i].getMethods();
for (int j = 0; j < methods.length; j++) {
Method method = methods[j];
int mods = method.getModifiers();
if (Modifier.isStatic(mods) || Modifier.isFinal(mods))
continue;
// make sure to generate only one instance of a particular
// method/signature.
String methodKey = getMethodSignature(method);
if (! generatedOverrides.containsKey(methodKey)) {
generateOverride(cfw, name, method);
generatedOverrides.put(methodKey, methodKey);
}
}
}
// Now, go through the superclasses methods, checking for abstract
// methods or additional methods to override.
FlattenedObject obj = jsObj != null
? new FlattenedObject(jsObj)
: null;
// generate any additional overrides that the object might contain.
Method[] methods = superClass.getMethods();
for (int j = 0; j < methods.length; j++) {
Method method = methods[j];
int mods = method.getModifiers();
if (Modifier.isStatic(mods) || Modifier.isFinal(mods))
continue;
// if a method is marked abstract, must implement it or the
// resulting class won't be instantiable. otherwise, if the object
// has a property of the same name, then an override is intended.
if (Modifier.isAbstract(mods) ||
(obj != null && obj.hasProperty(method.getName())))
{
// make sure to generate only one instance of a particular
// method/signature.
String methodKey = getMethodSignature(method);
if (! generatedOverrides.containsKey(methodKey)) {
generateOverride(cfw, name, method);
generatedOverrides.put(methodKey, method);
}
}
}
// TODO: generate Java methods, fields for remaining properties that
// are not overrides? Probably not necessary to generate methods
// that aren't overrides.
ByteArrayOutputStream out = new ByteArrayOutputStream(512);
try {
cfw.write(out);
}
catch (IOException ioe) {
throw new RuntimeException("unexpected IOException");
}
byte[] bytes = out.toByteArray();
if (nameHelper != null && nameHelper.getGeneratingDirectory() != null)
{
try {
int lastDot = name.lastIndexOf('.');
if (lastDot != -1)
name = name.substring(lastDot+1);
String filename = nameHelper.getTargetClassFileName(name);
FileOutputStream file = new FileOutputStream(filename);
file.write(bytes);
file.close();
}
catch (IOException iox) {
throw WrappedException.wrapException(iox);
}
return null;
}
SecuritySupport ss = cx.getSecuritySupport();
if (ss != null) {
Object securityDomain = cx.getSecurityDomainForStackDepth(-1);
return ss.defineClass(name, bytes, securityDomain);
} else {
if (classLoader == null)
classLoader = new MyClassLoader();
classLoader.defineClass(name, bytes);
return classLoader.loadClass(name, true);
}
}
/**
* Utility method, which dynamically binds a Context to the current thread,
* if none already exists.
*/
public static Object callMethod(FlattenedObject object, Object methodId,
Object[] args)
{
Context cx = Context.enter();
try {
if (object.hasProperty(methodId))
return object.callMethod(methodId, args);
} catch (PropertyException ex) {
// shouldn't occur
} catch (NotAFunctionException ex) {
// TODO: could occur
} catch (JavaScriptException ex) {
// TODO: could occur
/*
} catch (Exception ex) {
// TODO: wouldn't it be better to let the exception propagate
// up so that it could be dealt with by the calling code?
ex.printStackTrace(System.err);
throw new Error(ex.getMessage());
*/
} finally {
Context.exit();
}
return Context.getUndefinedValue();
}
private static void generateCtor(ClassFileWriter cfw, String name,
Class superClass)
{
cfw.startMethod("<init>",
"(Lorg/mozilla/javascript/FlattenedObject;)V",
ClassFileWriter.ACC_PUBLIC);
// Invoke base class constructor
cfw.add(ByteCode.ALOAD_0); // this
cfw.add(ByteCode.INVOKESPECIAL,
superClass.getName().replace('.', '/'),
"<init>", "()", "V");
/*
// Set the prototype of the js object to be a LiveConnect
// wapper of the generated class's object
cfw.add(ByteCode.ALOAD_1); // first arg
cfw.add(ByteCode.INVOKEVIRTUAL,
"org/mozilla/javascript/FlattenedObject",
"getObject", "()",
"Lorg/mozilla/javascript/Scriptable;");
cfw.add(ByteCode.ALOAD_0); // this
cfw.add(ByteCode.INVOKESTATIC,
"org/mozilla/javascript/ScriptRuntime",
"setAdapterProto",
"(Lorg/mozilla/javascript/Scriptable;" +
"Ljava/lang/Object;)",
"V");
*/
// Save parameter in instance variable
cfw.add(ByteCode.ALOAD_0); // this
cfw.add(ByteCode.ALOAD_1); // first arg
cfw.add(ByteCode.PUTFIELD, name, "o",
"Lorg/mozilla/javascript/FlattenedObject;");
// Store Scriptable object in "self", a public instance variable,
// so scripts can read it.
cfw.add(ByteCode.ALOAD_0); // this
cfw.add(ByteCode.ALOAD_1); // first arg
cfw.add(ByteCode.INVOKEVIRTUAL,
"org/mozilla/javascript/FlattenedObject",
"getObject", "()",
"Lorg/mozilla/javascript/Scriptable;");
cfw.add(ByteCode.PUTFIELD, name, "self",
"Lorg/mozilla/javascript/Scriptable;");
cfw.add(ByteCode.RETURN);
cfw.stopMethod((short)20, null); // TODO: magic number "20"
}
private static void generateEmptyCtor(ClassFileWriter cfw, String name,
Class superClass,
String scriptClassName)
{
cfw.startMethod("<init>", "()V", ClassFileWriter.ACC_PUBLIC);
// Invoke base class constructor
cfw.add(ByteCode.ALOAD_0); // this
cfw.add(ByteCode.INVOKESPECIAL,
superClass.getName().replace('.', '/'),
"<init>", "()", "V");
// Load script class
cfw.add(ByteCode.NEW, scriptClassName);
cfw.add(ByteCode.DUP);
cfw.add(ByteCode.INVOKESPECIAL, scriptClassName, "<init>", "()", "V");
// Run script and save resulting scope
cfw.add(ByteCode.INVOKESTATIC,
"org/mozilla/javascript/ScriptRuntime",
"runScript",
"(Lorg/mozilla/javascript/Script;)",
"Lorg/mozilla/javascript/FlattenedObject;");
cfw.add(ByteCode.ASTORE_1);
// Save the FlattenedObject in instance variable
cfw.add(ByteCode.ALOAD_0); // this
cfw.add(ByteCode.ALOAD_1); // the FlattenedObject
cfw.add(ByteCode.PUTFIELD, name, "o",
"Lorg/mozilla/javascript/FlattenedObject;");
// Store Scriptable object in "self", a public instance variable,
// so scripts can read it.
cfw.add(ByteCode.ALOAD_0); // this
cfw.add(ByteCode.ALOAD_1); // the FlattenedObject
cfw.add(ByteCode.INVOKEVIRTUAL,
"org/mozilla/javascript/FlattenedObject",
"getObject", "()",
"Lorg/mozilla/javascript/Scriptable;");
cfw.add(ByteCode.PUTFIELD, name, "self",
"Lorg/mozilla/javascript/Scriptable;");
// Set the prototype of the js object to be a LiveConnect
// wapper of the generated class's object
cfw.add(ByteCode.ALOAD_1); // the FlattenedObject
cfw.add(ByteCode.INVOKEVIRTUAL,
"org/mozilla/javascript/FlattenedObject",
"getObject", "()",
"Lorg/mozilla/javascript/Scriptable;");
cfw.add(ByteCode.ALOAD_0); // this
cfw.add(ByteCode.INVOKESTATIC,
"org/mozilla/javascript/ScriptRuntime",
"setAdapterProto",
"(Lorg/mozilla/javascript/Scriptable;" +
"Ljava/lang/Object;)",
"V");
cfw.add(ByteCode.RETURN);
cfw.stopMethod((short)20, null); // TODO: magic number "20"
}
/**
* Generates code to create a java.lang.Boolean, java.lang.Character or a
* java.lang.Double to wrap the specified primitive parameter. Leaves the
* wrapper object on the top of the stack.
*/
private static int generateWrapParam(ClassFileWriter cfw, int paramOffset,
Class paramType)
{
if (paramType.equals(Boolean.TYPE)) {
// wrap boolean values with java.lang.Boolean.
cfw.add(ByteCode.NEW, "java/lang/Boolean");
cfw.add(ByteCode.DUP);
cfw.add(ByteCode.ILOAD, paramOffset++);
cfw.add(ByteCode.INVOKESPECIAL, "java/lang/Boolean",
"<init>", "(Z)", "V");
} else
if (paramType.equals(Character.TYPE)) {
// Create a string of length 1 using the character parameter.
cfw.add(ByteCode.NEW, "java/lang/String");
cfw.add(ByteCode.DUP);
cfw.add(ByteCode.ICONST_1);
cfw.add(ByteCode.NEWARRAY, ByteCode.T_CHAR);
cfw.add(ByteCode.DUP);
cfw.add(ByteCode.ICONST_0);
cfw.add(ByteCode.ILOAD, paramOffset++);
cfw.add(ByteCode.CASTORE);
cfw.add(ByteCode.INVOKESPECIAL, "java/lang/String",
"<init>", "([C)", "V");
} else {
// convert all numeric values to java.lang.Double.
cfw.add(ByteCode.NEW, "java/lang/Double");
cfw.add(ByteCode.DUP);
String typeName = paramType.getName();
switch (typeName.charAt(0)) {
case 'b':
case 's':
case 'i':
// load an int value, convert to double.
cfw.add(ByteCode.ILOAD, paramOffset++);
cfw.add(ByteCode.I2D);
break;
case 'l':
// load a long, convert to double.
cfw.add(ByteCode.LLOAD, paramOffset);
cfw.add(ByteCode.L2D);
paramOffset += 2;
break;
case 'f':
// load a float, convert to double.
cfw.add(ByteCode.FLOAD, paramOffset++);
cfw.add(ByteCode.F2D);
break;
case 'd':
cfw.add(ByteCode.DLOAD, paramOffset);
paramOffset += 2;
break;
}
cfw.add(ByteCode.INVOKESPECIAL, "java/lang/Double",
"<init>", "(D)", "V");
}
return paramOffset;
}
/**
* Generates code to convert a wrapped value type to a primitive type.
* Handles unwrapping java.lang.Boolean, and java.lang.Number types.
* May need to map between char and java.lang.String as well.
* Generates the appropriate RETURN bytecode.
*/
private static void generateReturnResult(ClassFileWriter cfw,
Class retType)
{
// wrap boolean values with java.lang.Boolean, convert all other
// primitive values to java.lang.Double.
if (retType.equals(Boolean.TYPE)) {
cfw.add(ByteCode.INVOKESTATIC,
"org/mozilla/javascript/Context",
"toBoolean", "(Ljava/lang/Object;)",
"Z");
cfw.add(ByteCode.IRETURN);
} else if (retType.equals(Character.TYPE)) {
// characters are represented as strings in JavaScript.
// return the first character.
// first convert the value to a string if possible.
cfw.add(ByteCode.INVOKESTATIC,
"org/mozilla/javascript/Context",
"toString", "(Ljava/lang/Object;)",
"Ljava/lang/String;");
cfw.add(ByteCode.ICONST_0);
cfw.add(ByteCode.INVOKEVIRTUAL, "java/lang/String", "charAt",
"(I)", "C");
cfw.add(ByteCode.IRETURN);
} else if (retType.isPrimitive()) {
cfw.add(ByteCode.INVOKESTATIC,
"org/mozilla/javascript/Context",
"toNumber", "(Ljava/lang/Object;)",
"D");
String typeName = retType.getName();
switch (typeName.charAt(0)) {
case 'b':
case 's':
case 'i':
cfw.add(ByteCode.D2I);
cfw.add(ByteCode.IRETURN);
break;
case 'l':
cfw.add(ByteCode.D2L);
cfw.add(ByteCode.LRETURN);
break;
case 'f':
cfw.add(ByteCode.D2F);
cfw.add(ByteCode.FRETURN);
break;
case 'd':
cfw.add(ByteCode.DRETURN);
break;
default:
throw new RuntimeException("Unexpected return type " +
retType.toString());
}
} else if (retType.equals(String.class)) {
cfw.add(ByteCode.INVOKESTATIC,
"org/mozilla/javascript/Context",
"toString", "(Ljava/lang/Object;)",
"Ljava/lang/String;");
cfw.add(ByteCode.ARETURN);
} else if (retType.equals(Scriptable.class)) {
cfw.add(ByteCode.ALOAD_0); // load 'this' to find scope from
cfw.add(ByteCode.INVOKESTATIC,
"org/mozilla/javascript/Context",
"toObject",
"(Ljava/lang/Object;" +
"Lorg/mozilla/javascript/Scriptable;)",
"Lorg/mozilla/javascript/Scriptable;");
cfw.add(ByteCode.ARETURN);
} else {
// If it is a wrapped type, cast to Wrapper and call unwrap()
cfw.add(ByteCode.DUP);
cfw.add(ByteCode.INSTANCEOF, "org/mozilla/javascript/Wrapper");
// skip 3 for IFEQ, 3 for CHECKCAST, and 5 for INVOKEINTERFACE
cfw.add(ByteCode.IFEQ, 11);
cfw.add(ByteCode.CHECKCAST, "org/mozilla/javascript/Wrapper");
cfw.add(ByteCode.INVOKEINTERFACE,
"org/mozilla/javascript/Wrapper",
"unwrap", "()", "Ljava/lang/Object;");
// If Undefined, return null
cfw.add(ByteCode.DUP);
cfw.add(ByteCode.INSTANCEOF, "org/mozilla/javascript/Undefined");
// skip 3 for IFEQ, 1 for ACONST_NULL, 1 for ARETURN
cfw.add(ByteCode.IFEQ, 5);
cfw.add(ByteCode.ACONST_NULL);
cfw.add(ByteCode.ARETURN);
// Now cast to return type
String retTypeStr = retType.getName().replace('.', '/');
cfw.add(ByteCode.CHECKCAST, retTypeStr);
cfw.add(ByteCode.ARETURN);
}
}
private static void generateOverride(ClassFileWriter cfw, String genName,
Method m)
{
Class[] parms = m.getParameterTypes();
StringBuffer sb = new StringBuffer();
sb.append('(');
short arrayLocal = 1; // includes this.
for (int i = 0; i < parms.length; i++) {
Class type = parms[i];
appendTypeString(sb, type);
if (type.equals(Long.TYPE) || type.equals(Double.TYPE))
arrayLocal += 2;
else
arrayLocal += 1;
}
sb.append(')');
appendTypeString(sb, m.getReturnType());
String methodSignature = sb.toString();
// System.out.println("generating " + m.getName() + methodSignature);
// System.out.flush();
cfw.startMethod(m.getName(), methodSignature,
ClassFileWriter.ACC_PUBLIC);
cfw.add(ByteCode.BIPUSH, (byte) parms.length); // > 255 parms?
cfw.add(ByteCode.ANEWARRAY, "java/lang/Object");
cfw.add(ByteCode.ASTORE, arrayLocal);
// allocate a local variable to store the scope used to wrap native objects.
short scopeLocal = (short) (arrayLocal + 1);
boolean loadedScope = false;
int paramOffset = 1;
for (int i = 0; i < parms.length; i++) {
cfw.add(ByteCode.ALOAD, arrayLocal);
cfw.add(ByteCode.BIPUSH, i);
if (parms[i].isPrimitive()) {
paramOffset = generateWrapParam(cfw, paramOffset, parms[i]);
} else {
// An arbitary Java object; call Context.toObject to wrap in
// a Scriptable object
cfw.add(ByteCode.ALOAD, paramOffset++);
if (! loadedScope) {
// load this.self into a local the first time it's needed.
// it will provide the scope needed by Context.toObject().
cfw.add(ByteCode.ALOAD_0);
cfw.add(ByteCode.GETFIELD, genName, "self",
"Lorg/mozilla/javascript/Scriptable;");
cfw.add(ByteCode.ASTORE, scopeLocal);
loadedScope = true;
}
cfw.add(ByteCode.ALOAD, scopeLocal);
// Get argument Class
cfw.addLoadConstant(parms[i].getName());
cfw.add(ByteCode.INVOKESTATIC,
"java/lang/Class",
"forName",
"(Ljava/lang/String;)",
"Ljava/lang/Class;");
cfw.add(ByteCode.INVOKESTATIC,
"org/mozilla/javascript/Context",
"toObject",
"(Ljava/lang/Object;" +
"Lorg/mozilla/javascript/Scriptable;" +
"Ljava/lang/Class;)",
"Lorg/mozilla/javascript/Scriptable;");
}
cfw.add(ByteCode.AASTORE);
}
cfw.add(ByteCode.ALOAD_0); // this
cfw.add(ByteCode.GETFIELD, genName, "o",
"Lorg/mozilla/javascript/FlattenedObject;");
cfw.addLoadConstant(m.getName());
cfw.add(ByteCode.ALOAD, arrayLocal);
// go through utility method, which creates a Context to run the
// method in.
cfw.add(ByteCode.INVOKESTATIC,
"org/mozilla/javascript/JavaAdapter",
"callMethod",
"(Lorg/mozilla/javascript/FlattenedObject;" +
"Ljava/lang/Object;[Ljava/lang/Object;)",
"Ljava/lang/Object;");
Class retType = m.getReturnType();
if (retType.equals(Void.TYPE)) {
cfw.add(ByteCode.POP);
cfw.add(ByteCode.RETURN);
} else {
generateReturnResult(cfw, retType);
}
cfw.stopMethod((short)(scopeLocal + 1), null);
}
/**
* Returns a fully qualified method name concatenated with its signature.
*/
private static String getMethodSignature(Method method) {
Class[] parms = method.getParameterTypes();
StringBuffer sb = new StringBuffer();
sb.append(method.getName());
sb.append('(');
for (int i = 0; i < parms.length; i++) {
Class type = parms[i];
appendTypeString(sb, type);
}
sb.append(')');
appendTypeString(sb, method.getReturnType());
return sb.toString();
}
private static StringBuffer appendTypeString(StringBuffer sb, Class type)
{
while (type.isArray()) {
sb.append('[');
type = type.getComponentType();
}
if (type.isPrimitive()) {
if (type.equals(Boolean.TYPE)) {
sb.append('Z');
} else
if (type.equals(Long.TYPE)) {
sb.append('J');
} else {
String typeName = type.getName();
sb.append(Character.toUpperCase(typeName.charAt(0)));
}
} else {
sb.append('L');
sb.append(type.getName().replace('.', '/'));
sb.append(';');
}
return sb;
}
static final class MyClassLoader extends ClassLoader {
public Class defineClass(String name, byte data[]) {
return super.defineClass(name, data, 0, data.length);
}
protected Class loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
Class clazz = findLoadedClass(name);
if (clazz == null) {
ClassLoader loader = getClass().getClassLoader();
if (loader != null)
return loader.loadClass(name);
clazz = findSystemClass(name);
}
if (resolve)
resolveClass(clazz);
return clazz;
}
private java.util.Hashtable loaded;
}
private static int serial;
private static MyClassLoader classLoader;
}

View File

@@ -1,412 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
package org.mozilla.javascript;
import java.lang.reflect.*;
import java.util.Hashtable;
import java.util.Enumeration;
/**
*
* @author Mike Shaver
* @author Norris Boyd
* @see NativeJavaObject
* @see NativeJavaClass
*/
class JavaMembers {
JavaMembers(Scriptable scope, Class cl) {
this.members = new Hashtable(23);
this.staticMembers = new Hashtable(7);
this.cl = cl;
reflect(scope, cl);
}
boolean has(String name, boolean isStatic) {
Hashtable ht = isStatic ? staticMembers : members;
Object obj = ht.get(name);
if (obj != null) {
return true;
}
else {
Member member = this.findExplicitFunction(name, isStatic);
return member != null;
}
}
Object get(Scriptable scope, String name, Object javaObject,
boolean isStatic)
{
Hashtable ht = isStatic ? staticMembers : members;
Object member = ht.get(name);
if (!isStatic && member == null) {
// Try to get static member from instance (LC3)
member = staticMembers.get(name);
}
if (member == null) {
member = this.getExplicitFunction(scope, name,
javaObject, isStatic);
if (member == null)
return Scriptable.NOT_FOUND;
}
if (member instanceof Scriptable)
return member;
Field field = (Field) member;
Object rval;
try {
rval = field.get(isStatic ? null : javaObject);
} catch (IllegalAccessException accEx) {
throw new RuntimeException("unexpected IllegalAccessException "+
"accessing Java field");
}
// Need to wrap the object before we return it.
Class fieldType = field.getType();
scope = ScriptableObject.getTopLevelScope(scope);
return NativeJavaObject.wrap(scope, rval, fieldType);
}
Member findExplicitFunction(String name, boolean isStatic) {
Hashtable ht = isStatic ? staticMembers : members;
int sigStart = name.indexOf('(');
Object member = null;
Member[] methodsOrCtors = null;
NativeJavaMethod method = null;
boolean isCtor = (isStatic && sigStart == 0);
if (isCtor) {
// Explicit request for an overloaded constructor
methodsOrCtors = ctors;
}
else if (sigStart > 0) {
// Explicit request for an overloaded method
String trueName = name.substring(0,sigStart);
Object obj = ht.get(trueName);
if (!isStatic && obj == null) {
// Try to get static member from instance (LC3)
obj = staticMembers.get(trueName);
}
if (obj != null && obj instanceof NativeJavaMethod) {
method = (NativeJavaMethod)obj;
methodsOrCtors = method.getMethods();
}
}
if (methodsOrCtors != null) {
for (int i = 0; i < methodsOrCtors.length; i++) {
String nameWithSig =
NativeJavaMethod.signature(methodsOrCtors[i]);
if (name.equals(nameWithSig)) {
return methodsOrCtors[i];
}
}
}
return null;
}
Object getExplicitFunction(Scriptable scope,
String name,
Object javaObject,
boolean isStatic) {
Hashtable ht = isStatic ? staticMembers : members;
Object member = null;
Member methodOrCtor = this.findExplicitFunction(name, isStatic);
if (methodOrCtor != null) {
Scriptable prototype =
ScriptableObject.getFunctionPrototype(scope);
if (methodOrCtor instanceof Constructor) {
NativeJavaConstructor fun =
new NativeJavaConstructor((Constructor)methodOrCtor);
fun.setParentScope(scope);
fun.setPrototype(prototype);
member = fun;
ht.put(name, fun);
}
else {
String trueName = methodOrCtor.getName();
member = ht.get(trueName);
if (member instanceof NativeJavaMethod &&
((NativeJavaMethod)member).getMethods().length > 1 ) {
NativeJavaMethod fun =
new NativeJavaMethod((Method)methodOrCtor, name);
fun.setParentScope(scope);
fun.setPrototype(prototype);
ht.put(name, fun);
member = fun;
}
}
}
return member;
}
public void put(String name, Object javaObject, Object value,
boolean isStatic)
{
Hashtable ht = isStatic ? staticMembers : members;
Object member = ht.get(name);
if (!isStatic && member == null) {
// Try to get static member from instance (LC3)
member = staticMembers.get(name);
}
if (member == null)
throw reportMemberNotFound(name);
if (member instanceof FieldAndMethods) {
member = ((FieldAndMethods) ht.get(name)).getField();
}
Field field = null;
try {
field = (Field) member;
field.set(javaObject, NativeJavaObject.coerceType(field.getType(),
value));
} catch (ClassCastException e) {
Object errArgs[] = { name };
throw Context.reportRuntimeError(Context.getMessage
("msg.java.method.assign",
errArgs));
} catch (IllegalAccessException accessEx) {
throw new RuntimeException("unexpected IllegalAccessException "+
"accessing Java field");
} catch (IllegalArgumentException argEx) {
Object errArgs[] = { value.getClass().getName(), field,
javaObject.getClass().getName() };
throw Context.reportRuntimeError(Context.getMessage(
"msg.java.internal.field.type", errArgs));
}
}
Object[] getIds(boolean isStatic) {
Hashtable ht = isStatic ? staticMembers : members;
int len = ht.size();
Object[] result = new Object[len];
Enumeration keys = ht.keys();
for (int i=0; i < len; i++)
result[i] = keys.nextElement();
return result;
}
Class getReflectedClass() {
return cl;
}
void reflectField(Field field) {
int mods = field.getModifiers();
if (!Modifier.isPublic(mods))
return;
boolean isStatic = Modifier.isStatic(mods);
Hashtable ht = isStatic ? staticMembers : members;
String name = field.getName();
Object member = ht.get(name);
if (member != null) {
if (member instanceof NativeJavaMethod) {
NativeJavaMethod method = (NativeJavaMethod) member;
Hashtable fmht = isStatic ? staticFieldAndMethods
: fieldAndMethods;
if (fmht == null) {
fmht = new Hashtable(11);
if (isStatic)
staticFieldAndMethods = fmht;
else
fieldAndMethods = fmht;
}
FieldAndMethods fam = new FieldAndMethods(method.getMethods(),
field);
fmht.put(name, fam);
ht.put(name, fam);
return;
}
if (member instanceof Field) {
Field oldField = (Field) member;
// beard:
// If an exception is thrown here, then JDirect classes on MRJ can't be used. JDirect
// classes implement multiple interfaces that each have a static "libraryInstance" field.
if (false) {
throw new RuntimeException("cannot have multiple Java " +
"fields with same name");
}
// If this newly reflected field shadows an inherited field, then replace it. Otherwise,
// since access to the field would be ambiguous from Java, no field should be reflected.
// For now, the first field found wins, unless another field explicitly shadows it.
if (oldField.getDeclaringClass().isAssignableFrom(field.getDeclaringClass()))
ht.put(name, field);
return;
}
throw new RuntimeException("unknown member type");
}
ht.put(name, field);
}
void reflectMethod(Scriptable scope, Method method) {
int mods = method.getModifiers();
if (!Modifier.isPublic(mods))
return;
boolean isStatic = Modifier.isStatic(mods);
Hashtable ht = isStatic ? staticMembers : members;
String name = method.getName();
NativeJavaMethod fun = (NativeJavaMethod) ht.get(name);
if (fun == null) {
fun = new NativeJavaMethod();
fun.setParentScope(scope);
fun.setPrototype(ScriptableObject.getFunctionPrototype(scope));
ht.put(name, fun);
fun.add(method);
}
else
fun.add(method);
}
void reflect(Scriptable scope, Class cl) {
// We reflect methods first, because we want overloaded field/method
// names to be allocated to the NativeJavaMethod before the field
// gets in the way.
Method[] methods = cl.getMethods();
for (int i = 0; i < methods.length; i++)
reflectMethod(scope, methods[i]);
Field[] fields = cl.getFields();
for (int i = 0; i < fields.length; i++)
reflectField(fields[i]);
ctors = cl.getConstructors();
}
Hashtable getFieldAndMethodsObjects(Object javaObject, boolean isStatic) {
Hashtable ht = isStatic ? staticFieldAndMethods : fieldAndMethods;
if (ht == null)
return null;
int len = ht.size();
Hashtable result = new Hashtable(len);
Enumeration e = ht.elements();
while (len-- > 0) {
FieldAndMethods fam = (FieldAndMethods) e.nextElement();
fam = (FieldAndMethods) fam.clone();
fam.setJavaObject(javaObject);
result.put(fam.getName(), fam);
}
return result;
}
Constructor[] getConstructors() {
return ctors;
}
static JavaMembers lookupClass(Scriptable scope, Class dynamicType,
Class staticType)
{
Class cl = dynamicType;
JavaMembers members = (JavaMembers) classTable.get(cl);
if (members != null)
return members;
if (staticType != null && staticType != dynamicType &&
!Modifier.isPublic(dynamicType.getModifiers()) &&
Modifier.isPublic(staticType.getModifiers()))
{
cl = staticType;
}
synchronized (classTable) {
members = (JavaMembers) classTable.get(cl);
if (members != null)
return members;
try {
members = new JavaMembers(scope, cl);
} catch (SecurityException e) {
// Reflection may fail for objects that are in a restricted
// access package (e.g. sun.*). If we get a security
// exception, try again with the static type. Otherwise,
// rethrow the exception.
if (cl != staticType)
members = new JavaMembers(scope, staticType);
else
throw e;
}
classTable.put(cl, members);
return members;
}
}
RuntimeException reportMemberNotFound(String memberName) {
Object errArgs[] = { cl.getName(),
memberName };
return Context.reportRuntimeError(
Context.getMessage("msg.java.member.not.found",
errArgs));
}
private static Hashtable classTable = new Hashtable();
private Class cl;
private Hashtable members;
private Hashtable fieldAndMethods;
private Hashtable staticMembers;
private Hashtable staticFieldAndMethods;
private Constructor[] ctors;
}
class FieldAndMethods extends NativeJavaMethod {
FieldAndMethods(Method[] methods, Field field) {
super(methods);
this.field = field;
}
void setJavaObject(Object javaObject) {
this.javaObject = javaObject;
}
String getName() {
return field.getName();
}
Field getField() {
return field;
}
public Object getDefaultValue(Class hint) {
if (hint == ScriptRuntime.FunctionClass)
return this;
Object rval;
try {
rval = field.get(javaObject);
} catch (IllegalAccessException accEx) {
throw Context.reportRuntimeError(Context.getMessage
("msg.java.internal.private", null));
}
rval = NativeJavaObject.wrap(this, rval, field.getType());
if (rval instanceof Scriptable) {
((Scriptable)rval).setParentScope(this);
((Scriptable)rval).setPrototype(parent.getPrototype());
}
return rval;
}
public Object clone() {
FieldAndMethods result = new FieldAndMethods(methods, field);
result.javaObject = javaObject;
return result;
}
private Field field;
private Object javaObject;
}

View File

@@ -1,92 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
// API class
package org.mozilla.javascript;
import java.lang.reflect.InvocationTargetException;
/**
* Java reflection of JavaScript exceptions. (Possibly wrapping a Java exception.)
*
* @author Mike McCabe
*/
public class JavaScriptException extends Exception {
/**
* Create a JavaScript exception wrapping the given JavaScript value.
*
* Instances of this class are thrown by the JavaScript 'throw' keyword.
*
* @param value the JavaScript value thrown.
*/
public JavaScriptException(Object value) {
super(ScriptRuntime.toString(value));
this.value = value;
}
/**
* Get the exception message.
*
* <p>Will just convert the wrapped exception to a string.
*/
public String getMessage() {
return ScriptRuntime.toString(value);
}
static JavaScriptException wrapException(Scriptable scope,
Throwable exn)
{
if (exn instanceof InvocationTargetException)
exn = ((InvocationTargetException)exn).getTargetException();
if (exn instanceof JavaScriptException)
return (JavaScriptException)exn;
Object wrapper = NativeJavaObject.wrap(scope, exn, Throwable.class);
return new JavaScriptException(wrapper);
}
/**
* Get the exception value originally thrown. This may be a
* JavaScript value (null, undefined, Boolean, Number, String,
* Scriptable or Function) or a Java exception value thrown from a
* host object or from Java called through LiveConnect.
*
* @return the value wrapped by this exception
*/
public Object getValue() {
if (value != null && value instanceof Wrapper)
// this will also catch NativeStrings...
return ((Wrapper)value).unwrap();
else
return value;
}
/**
* The JavaScript exception value. This value is not
* intended for general use; if the JavaScriptException wraps a
* Java exception, getScriptableValue may return a Scriptable
* wrapping the original Java exception object.
*
* We would prefer to go through a getter to encapsulate the value,
* however that causes the bizarre error "nanosecond timeout value
* out of range" on the MS JVM.
* @serial
*/
Object value;
}

View File

@@ -1,87 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
package org.mozilla.javascript;
public class Label {
private static final int FIXUPTABLE_SIZE = 8;
private static final boolean DEBUG = true;
public Label()
{
itsPC = -1;
}
public short getPC()
{
return itsPC;
}
public void fixGotos(byte theCodeBuffer[])
{
if (DEBUG) {
if ((itsPC == -1) && (itsFixupTable != null))
throw new RuntimeException("Unlocated label");
}
if (itsFixupTable != null) {
for (int i = 0; i < itsFixupTableTop; i++) {
int fixupSite = itsFixupTable[i];
// -1 to get delta from instruction start
short offset = (short)(itsPC - (fixupSite - 1));
theCodeBuffer[fixupSite++] = (byte)(offset >> 8);
theCodeBuffer[fixupSite] = (byte)offset;
}
}
itsFixupTable = null;
}
public void setPC(short thePC)
{
if (DEBUG) {
if ((itsPC != -1) && (itsPC != thePC))
throw new RuntimeException("Duplicate label");
}
itsPC = thePC;
}
public void addFixup(int fixupSite)
{
if (itsFixupTable == null) {
itsFixupTableTop = 1;
itsFixupTable = new int[FIXUPTABLE_SIZE];
itsFixupTable[0] = fixupSite;
}
else {
if (itsFixupTableTop == itsFixupTable.length) {
int oldLength = itsFixupTable.length;
int newTable[] = new int[oldLength + FIXUPTABLE_SIZE];
System.arraycopy(itsFixupTable, 0, newTable, 0, oldLength);
itsFixupTable = newTable;
}
itsFixupTable[itsFixupTableTop++] = fixupSite;
}
}
private short itsPC;
private int itsFixupTable[];
private int itsFixupTableTop;
}

View File

@@ -1,63 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
package org.mozilla.javascript;
public class LabelTable {
private static final boolean DEBUGLABELS = false;
private static final int LabelTableSize = 32;
protected Label itsLabelTable[];
protected int itsLabelTableTop;
public int acquireLabel()
{
if (itsLabelTable == null) {
itsLabelTable = new Label[LabelTableSize];
itsLabelTable[0] = new Label();
itsLabelTableTop = 1;
return 0x80000000;
}
else {
if (itsLabelTableTop == itsLabelTable.length) {
Label oldTable[] = itsLabelTable;
itsLabelTable = new Label[itsLabelTableTop * 2];
System.arraycopy(oldTable, 0, itsLabelTable, 0, itsLabelTableTop);
}
itsLabelTable[itsLabelTableTop] = new Label();
int result = itsLabelTableTop++;
return result | 0x80000000;
}
}
public int markLabel(int theLabel, int pc)
{
if (DEBUGLABELS) {
if ((theLabel & 0x80000000) != 0x80000000)
throw new RuntimeException("Bad label, no biscuit");
}
theLabel &= 0x7FFFFFFF;
if (DEBUGLABELS) {
System.out.println("Marking label " + theLabel + " at " + pc);
}
itsLabelTable[theLabel].setPC((short)pc);
return theLabel | 0x80000000;
}
}

View File

@@ -1,350 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
package org.mozilla.javascript;
import java.io.Reader;
import java.io.IOException;
/**
* An input buffer that combines fast character-based access with
* (slower) support for retrieving the text of the current line. It
* also supports building strings directly out of the internal buffer
* to support fast scanning with minimal object creation.
*
* Note that it is customized in several ways to support the
* TokenStream class, and should not be considered general.
*
* Credits to Kipp Hickman and John Bandhauer.
*
* @author Mike McCabe
*/
final class LineBuffer {
/*
* for smooth operation of getLine(), this should be greater than
* the length of any expected line. Currently, 256 is 3% slower
* than 4096 for large compiles, but seems safer given evaluateString.
* Strings for the scanner are are built with StringBuffers
* instead of directly out of the buffer whenever a string crosses
* a buffer boundary, so small buffer sizes will mean that more
* objects are created.
*/
static final int BUFLEN = 256;
LineBuffer(Reader in, int lineno) {
this.in = in;
this.lineno = lineno;
}
int read() throws IOException {
if (end == offset && !fill())
return -1;
// Do only a bitmask + branch per character, at the cost of
// three branches per low-bits-only character.
if ((buffer[offset] & '\ufff0') == 0) {
if (buffer[offset] == '\r') {
// if the next character is a newline, skip past it.
if ((offset + 1) < end) {
if (buffer[offset + 1] == '\n')
offset++;
} else {
// set a flag for fill(), in case the first char of the
// next fill is a newline.
lastWasCR = true;
}
}
else if (buffer[offset] != '\n') {
return (int) buffer[offset++];
}
offset++;
prevStart = lineStart;
lineStart = offset;
lineno++;
return '\n';
}
return (int) buffer[offset++];
}
void unread() {
if (offset == 0)
// We can get here when we're asked to unread() an
// implicit EOF_CHAR.
// This would also be wrong behavior in the general case,
// because a peek() could map a buffer.length offset to 0
// in the process of a fill(), and leave it there. But
// the scanner never calls peek() or a failed match()
// followed by unread()... this would violate 1-character
// lookahead. So we're OK.
return;
offset--;
if ((buffer[offset] & '\ufff0') == 0
&& (buffer[offset] == '\r' || buffer[offset] == '\n')) {
// back off from the line start we presumably just registered...
lineStart = prevStart;
lineno--;
}
}
int peek() throws IOException {
if (end == offset && !fill())
return -1;
if (buffer[offset] == '\r')
return '\n';
return buffer[offset];
}
boolean match(char c) throws IOException {
if (end == offset && !fill())
return false;
// This'd be a place where we'd need to map '\r' to '\n' and
// do other updates, but TokenStream never looks ahead for
// '\n', so we don't bother.
if (buffer[offset] == c) {
offset++;
return true;
}
return false;
}
// Reconstruct a source line from the buffers. This can be slow...
String getLine() {
StringBuffer result = new StringBuffer();
int start = lineStart;
if (start >= offset) {
// the line begins somewhere in the other buffer; get that first.
if (otherStart < otherEnd)
// if a line ending was seen in the other buffer... otherwise
// just ignore this strange case.
result.append(otherBuffer, otherStart,
otherEnd - otherStart);
start = 0;
}
// get the part of the line in the current buffer.
result.append(buffer, start, offset - start);
// Get the remainder of the line.
int i = offset;
while(true) {
if (i == buffer.length) {
// we're out of buffer, let's just expand it. We do
// this instead of reading into a StringBuffer to
// preserve the stream for later reads.
char[] newBuffer = new char[buffer.length * 2];
System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);
buffer = newBuffer;
int charsRead = 0;
try {
charsRead = in.read(buffer, end, buffer.length - end);
} catch (IOException ioe) {
// ignore it, we're already displaying an error...
}
if (charsRead < 0)
break;
end += charsRead;
}
if (buffer[i] == '\r' || buffer[i] == '\n')
break;
i++;
}
result.append(buffer, offset, i - offset);
return result.toString();
}
// Get the offset of the current character, relative to
// the line that getLine() returns.
int getOffset() {
if (lineStart >= offset)
// The line begins somewhere in the other buffer.
return offset + (otherEnd - otherStart);
else
return offset - lineStart;
}
// Set a mark to indicate that the reader should begin
// accumulating characters for getString(). The string begins
// with the last character read.
void startString() {
if (offset == 0) {
// We can get here if startString is called after a peek()
// or failed match() with offset past the end of the
// buffer.
// We're at the beginning of the buffer, and the previous character
// (which we want to include) is at the end of the last one, so
// we just go to StringBuffer mode.
stringSoFar = new StringBuffer();
stringSoFar.append(otherBuffer, otherEnd - 1, 1);
stringStart = -1; // Set sentinel value.
} else {
// Support restarting strings
stringSoFar = null;
stringStart = offset - 1;
}
}
// Get a string consisting of the characters seen since the last
// startString.
String getString() {
String result;
/*
* There's one strange case here: If the character offset currently
* points to (which we never want to include in the string) is
* a newline, then if the previous character is a carriage return,
* we probably want to exclude that as well. If the offset is 0,
* then we hope that fill() handled excluding it from stringSoFar.
*/
int loseCR = (offset > 0 &&
buffer[offset] == '\n' && buffer[offset - 1] == '\r') ?
1 : 0;
if (stringStart != -1) {
// String mark is valid, and in this buffer.
result = new String(buffer, stringStart,
offset - stringStart - loseCR);
} else {
if (stringSoFar == null)
stringSoFar = new StringBuffer();
// Exclude cr as well as nl of newline. If offset is 0, then
// hopefully fill() did the right thing.
result = (stringSoFar.append(buffer, 0, offset - loseCR)).toString();
}
stringStart = -1;
stringSoFar = null;
return result;
}
boolean fill() throws IOException {
// not sure I care...
if (end - offset != 0)
throw new IOException("fill of non-empty buffer");
// If there's a string currently being accumulated, save
// off the progress.
/*
* Exclude an end-of-buffer carriage return. NOTE this is not
* fully correct in the general case, because we really only
* want to exclude the carriage return if it's followed by a
* linefeed at the beginning of the next buffer. But we fudge
* because the scanner doesn't do this.
*/
int loseCR = (offset > 0 && lastWasCR) ? 1 : 0;
if (stringStart != -1) {
// The mark is in the current buffer, save off from the mark to the
// end.
stringSoFar = new StringBuffer();
stringSoFar.append(buffer, stringStart, end - stringStart - loseCR);
stringStart = -1;
} else if (stringSoFar != null) {
// the string began prior to the current buffer, so save the
// whole current buffer.
stringSoFar.append(buffer, 0, end - loseCR);
}
// swap buffers
char[] tempBuffer = buffer;
buffer = otherBuffer;
otherBuffer = tempBuffer;
// allocate the buffers lazily, in case we're handed a short string.
if (buffer == null) {
buffer = new char[BUFLEN];
}
// buffers have switched, so move the newline marker.
otherStart = lineStart;
otherEnd = end;
// set lineStart to a sentinel value, unless this is the first
// time around.
prevStart = lineStart = (otherBuffer == null) ? 0 : buffer.length + 1;
offset = 0;
end = in.read(buffer, 0, buffer.length);
if (end < 0) {
end = 0;
// can't null buffers here, because a string might be retrieved
// out of the other buffer, and a 0-length string might be
// retrieved out of this one.
hitEOF = true;
return false;
}
// If the last character of the previous fill was a carriage return,
// then ignore a newline.
// There's another bizzare special case here. If lastWasCR is
// true, and we see a newline, and the buffer length is
// 1... then we probably just read the last character of the
// file, and returning after advancing offset is not the right
// thing to do. Instead, we try to ignore the newline (and
// likely get to EOF for real) by doing yet another fill().
if (lastWasCR) {
if (buffer[0] == '\n') {
offset++;
if (end == 1)
return fill();
}
lineStart = offset;
lastWasCR = false;
}
return true;
}
int getLineno() { return lineno; }
boolean eof() { return hitEOF; }
private Reader in;
private char[] otherBuffer = null;
private char[] buffer = null;
// Yes, there are too too many of these.
private int offset = 0;
private int end = 0;
private int otherEnd;
private int lineno;
private int lineStart = 0;
private int otherStart = 0;
private int prevStart = 0;
private boolean lastWasCR = false;
private boolean hitEOF = false;
private int stringStart = -1;
private StringBuffer stringSoFar = null;
}

View File

@@ -1,96 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.0 (the "MozPL"); you may not use this file except in
* compliance with the MozPL. You may obtain a copy of the MozPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the MozPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the MozPL
* for the specific language governing rights and limitations under the
* MozPL.
*
* The Initial Developer of this code under the MozPL is Ian D. Stewart.
* Portions created by Ian D. Stewart are Copyright (C) 1998, 1999
* Ian D. Stewart.
* All Rights Reserved.
*/
/* This class provides a series of methods for accessing event listeners. */
package org.mozilla.javascript;
import java.util.Enumeration;
import java.util.Vector;
/**
* This class acts as central storage location for miscelanious
* event listeners. It provides methods for adding, removing
* and accessing listeners, both individually and collectively,
* by the listener interface implemented
*
* Note: This class performs the same functions as
* javax.swing.event.EventListenerList, and is provided
* primarily for implementations lacking the Swing packages
*
* @author Ian D. Stewart
* @since JavaScript-Java 1.4 rel 3
*/
public class ListenerCollection extends Vector {
/**
* Create a new ListenerCollection
*/
public ListenerCollection() {
super();
} // Constructor
/**
* Add a new listener to the collection
* @param listener the listener
*/
public void addListener(Object listener) {
this.addElement(listener);
}
/**
* Remove a listener from the collection
* @param listener the listener
*/
public void removeListener(Object listener) {
this.removeElement(listener);
}
/**
* Returns an Enumeration of all the listeners
* being stored in this collection
* @return an Enumeration of all listeners
*/
public Enumeration getAllListeners() {
return this.elements();
}
/**
* Return all the listeners in this collection which
* implement the specified interface
*
* @param iface the interface
* @return an array of listeners which implement the given
* interface
*/
public Object[] getListeners(Class iface) {
Vector array = new Vector();
for(Enumeration enum = getAllListeners();enum.hasMoreElements();) {
Object listener = enum.nextElement();
if(iface.isInstance(listener)) {
array.addElement(listener);
}
}
Object[] result = new Object[array.size()];
array.copyInto(result);
return result;
}
} // ListenerCollection
// end of ListenerCollection.java ...

View File

@@ -1,62 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
package org.mozilla.javascript;
public class LocalVariable {
public LocalVariable(String name, boolean isParameter) {
itsName = name;
itsIsParameter = isParameter;
}
public void setIndex(int index){ itsIndex = index; }
public int getIndex() { return itsIndex; }
public void setIsParameter() { itsIsParameter = true; }
public boolean isParameter() { return itsIsParameter; }
public String getName() { return itsName; }
/**
* Return the starting PC where this variable is live, or -1
* if it is not a Java register.
*/
public int getStartPC() {
return -1;
}
/**
* Return the Java register number or -1 if it is not a Java register.
*/
public short getJRegister() {
return -1;
}
/**
* Return true if the local variable is a Java register with double type.
*/
public boolean isNumber() {
return false;
}
private String itsName;
private int itsIndex = -1;
private boolean itsIsParameter;
}

View File

@@ -1,214 +0,0 @@
#! gmake
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (the "NPL"); you may not use this file except in
# compliance with the NPL. You may obtain a copy of the NPL at
# http://www.mozilla.org/NPL/
#
# Software distributed under the NPL is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
# for the specific language governing rights and limitations under the
# NPL.
#
# The Initial Developer of this code under the NPL is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1999 Netscape Communications Corporation. All Rights
# Reserved.
#
# Makefile the core javascript classes.
#
# This Makefile is intended to be called from the toplevel Makefile.
#
# List files explicitly to exclude .java files in this dir we don't want
# to compile. Also hack in classfile stuff...
SOURCES = \
$(PATH_PREFIX)/Arguments.java \
$(PATH_PREFIX)/BinaryDigitReader.java \
$(PATH_PREFIX)/ClassDefinitionException.java \
$(PATH_PREFIX)/ClassNameHelper.java \
$(PATH_PREFIX)/Context.java \
$(PATH_PREFIX)/DeepBytecodeHook.java \
$(PATH_PREFIX)/DeepCallHook.java \
$(PATH_PREFIX)/DeepErrorReporterHook.java \
$(PATH_PREFIX)/DeepExecuteHook.java \
$(PATH_PREFIX)/DeepNewObjectHook.java \
$(PATH_PREFIX)/DeepScriptHook.java \
$(PATH_PREFIX)/DefaultErrorReporter.java \
$(PATH_PREFIX)/ErrorReporter.java \
$(PATH_PREFIX)/EvaluatorException.java \
$(PATH_PREFIX)/FlattenedObject.java \
$(PATH_PREFIX)/Function.java \
$(PATH_PREFIX)/FunctionNode.java \
$(PATH_PREFIX)/FunctionObject.java \
$(PATH_PREFIX)/IRFactory.java \
$(PATH_PREFIX)/ImporterTopLevel.java \
$(PATH_PREFIX)/InterpretedFunction.java \
$(PATH_PREFIX)/InterpretedScript.java \
$(PATH_PREFIX)/Interpreter.java \
$(PATH_PREFIX)/InterpreterData.java \
$(PATH_PREFIX)/JavaAdapter.java \
$(PATH_PREFIX)/JavaMembers.java \
$(PATH_PREFIX)/JavaScriptException.java \
$(PATH_PREFIX)/Label.java \
$(PATH_PREFIX)/LabelTable.java \
$(PATH_PREFIX)/LineBuffer.java \
$(PATH_PREFIX)/ListenerCollection.java \
$(PATH_PREFIX)/LocalVariable.java \
$(PATH_PREFIX)/NativeArray.java \
$(PATH_PREFIX)/NativeBoolean.java \
$(PATH_PREFIX)/NativeCall.java \
$(PATH_PREFIX)/NativeClosure.java \
$(PATH_PREFIX)/NativeDate.java \
$(PATH_PREFIX)/NativeFunction.java \
$(PATH_PREFIX)/NativeGlobal.java \
$(PATH_PREFIX)/NativeJavaArray.java \
$(PATH_PREFIX)/NativeJavaClass.java \
$(PATH_PREFIX)/NativeJavaConstructor.java \
$(PATH_PREFIX)/NativeJavaMethod.java \
$(PATH_PREFIX)/NativeJavaObject.java \
$(PATH_PREFIX)/NativeJavaPackage.java \
$(PATH_PREFIX)/NativeMath.java \
$(PATH_PREFIX)/NativeNumber.java \
$(PATH_PREFIX)/NativeObject.java \
$(PATH_PREFIX)/NativeScript.java \
$(PATH_PREFIX)/NativeString.java \
$(PATH_PREFIX)/NativeWith.java \
$(PATH_PREFIX)/Node.java \
$(PATH_PREFIX)/NodeTransformer.java \
$(PATH_PREFIX)/NotAFunctionException.java \
$(PATH_PREFIX)/Parser.java \
$(PATH_PREFIX)/PreorderNodeIterator.java \
$(PATH_PREFIX)/PropertyException.java \
$(PATH_PREFIX)/RegExpProxy.java \
$(PATH_PREFIX)/Script.java \
$(PATH_PREFIX)/ScriptRuntime.java \
$(PATH_PREFIX)/Scriptable.java \
$(PATH_PREFIX)/ScriptableObject.java \
$(PATH_PREFIX)/SecuritySupport.java \
$(PATH_PREFIX)/ShallowNodeIterator.java \
$(PATH_PREFIX)/SourceTextItem.java \
$(PATH_PREFIX)/SourceTextManager.java \
$(PATH_PREFIX)/TokenStream.java \
$(PATH_PREFIX)/Undefined.java \
$(PATH_PREFIX)/VariableTable.java \
$(PATH_PREFIX)/WrappedException.java \
$(PATH_PREFIX)/Wrapper.java \
$(PATH_PREFIX)/regexp/NativeRegExp.java \
$(PATH_PREFIX)/regexp/NativeRegExpCtor.java \
$(PATH_PREFIX)/regexp/RegExpImpl.java \
$(PATH_PREFIX)/regexp/SubString.java \
$(PATH_PREFIX)/../classfile/ByteCode.java \
$(PATH_PREFIX)/../classfile/ClassFileWriter.java\
$(NULL)
RESOURCEDIR = $(PATH_PREFIX)/resources
RESOURCES = $(RESOURCEDIR)/*.properties
# This must be evaluated in some context where the classes can be
# found; we can't use a simple translation from sources, because a
# .java file may produce more than one .class file.
# (use org/mozilla/classfile explicitly for now; should be replaced
# with something parameterized, but jar doesn't understand ..)
CLASSES = $(PATH_PREFIX)/*.class $(PATH_PREFIX)/regexp/*.class \
org/mozilla/classfile/*.class
# A class or set of classes as visible from the top level. For want
# of ${subst ,,}. This variable is only used to trigger dependency
# analysis, and multiple words confuse gmake, so it can be smaller
# than the full set of sources. (We assume we'll never need to do the
# same thing with RESOURCES.)
TLCLASS = $(CLASSDIR)/$(PATH_PREFIX)/*.class
# An empty file, used mainly for timestamp/dependency purposes by
# "fast" builds
FASTTARGET=$(CLASSDIR)/.lastbuild
$(JAR) : $(TLCLASS) $(CLASSDIR)/$(RESOURCES)
cd $(CLASSDIR) ; \
jar cf ../$(JAR) $(CLASSES) $(RESOURCES)
$(TLCLASS) : $(SOURCES)
- mkdir -p $(CLASSDIR)
echo "" > $(FASTTARGET)
$(JAVAC) $(JFLAGS) -d $(CLASSDIR) $(SOURCES)
$(CLASSDIR)/$(RESOURCES) : $(RESOURCES)
- mkdir -p $(CLASSDIR)/$(RESOURCEDIR)
cp $(RESOURCES) $(CLASSDIR)/$(RESOURCEDIR)
# Since the jar file is a target for regular builds, "fast" builds use a
# dummy file, updated before each compilation to provide a timestamp.
# Even so, using a dummy file is far from foolproof, so we still need
# the regular build.
fast: $(FASTTARGET)
# So that we recompile only the files that have changed, we pretend
# the only real dependencies are the source files, and recopy the
# resources every time. Right now (14 Jun 99), the only resource is
# Messages.properties, so it's a small price to pay.
$(FASTTARGET) : $(SOURCES)
- mkdir -p $(CLASSDIR)/$(RESOURCEDIR)
cp $(RESOURCES) $(CLASSDIR)/$(RESOURCEDIR)
echo "" > $(FASTTARGET)
$(JAVAC) $(JFLAGS) -d $(CLASSDIR) $(?)
cd $(CLASSDIR) ; \
jar cf ../$(JAR) $(CLASSES) $(RESOURCES)
clean :
- cd $(CLASSDIR) && rm $(CLASSES) $(RESOURCES)
- rm $(PATH_PREFIX)/message.ids \
$(PATH_PREFIX)/property.ids \
$(PATH_PREFIX)/MANIFEST
clobber : clean
-rm $(JAR)
$(PATH_PREFIX)/MANIFEST : $(SOURCES) $(RESOURCES) $(PATH_PREFIX)/Makefile
ls $(SOURCES) $(RESOURCES) $(PATH_PREFIX)/Makefile \
> $(@)
# A sed/grep regular expression.
MESSAGE_PREFIX = msg\.
# Find all the msg.* strings in the source, and condense them to a sorted list,
# excluding duplicates.
# We cheat here and also look at messages in the optimizer subdir.
# (regexp subdir sources are already included in $(SOURCES)
MORE_SOURCES = $(SOURCES) $(PATH_PREFIX)/optimizer/*.java
$(PATH_PREFIX)/message.ids : $(SOURCES)
grep '$(MESSAGE_PREFIX)' $(MORE_SOURCES) |\
sed -e 's/.*\"\($(MESSAGE_PREFIX)\)\([^\"]*\).*/\1\2/' | \
sort | uniq > $(PATH_PREFIX)/message.ids
# Find all the msg.* strings in the resource files, and condense them to a
# sorted list, not excluding duplicates.
$(PATH_PREFIX)/property.ids : $(RESOURCES)
grep '^$(MESSAGE_PREFIX)' $(RESOURCES) |\
sed -e 's/.*\($(MESSAGE_PREFIX)\)\([^ =]*\).*/\1\2/' |\
sort > $(PATH_PREFIX)/property.ids
# Compare the resulting message.ids and property.ids files and confirm
# that they do not differ. This means that every message string used
# in the source is defined somewhere in the resource file, every
# resource in the resource file is used somewhere in the source, and
# no resource is defined more than once.
check : $(PATH_PREFIX)/message.ids $(PATH_PREFIX)/property.ids FORCE
- diff $(PATH_PREFIX)/message.ids $(PATH_PREFIX)/property.ids
# look for unmatched single quotes ... seems to fail when none!
# - sed -e s/\'\'//g $($RESOURCES) | grep \'
# Emulate .PHONY
FORCE :

View File

@@ -1,921 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
package org.mozilla.javascript;
import java.util.Hashtable;
/**
* This class implements the Array native object.
* @author Norris Boyd
* @author Mike McCabe
*/
public class NativeArray extends ScriptableObject {
/*
* Optimization possibilities and open issues:
* - Long vs. double schizophrenia. I suspect it might be better
* to use double throughout.
* - Most array operations go through getElem or setElem (defined
* in this file) to handle the full 2^32 range; it might be faster
* to have versions of most of the loops in this file for the
* (infinitely more common) case of indices < 2^31.
* - Functions that need a new Array call "new Array" in the
* current scope rather than using a hardwired constructor;
* "Array" could be redefined. It turns out that js calls the
* equivalent of "new Array" in the current scope, except that it
* always gets at least an object back, even when Array == null.
*/
/**
* Zero-parameter constructor: just used to create Array.prototype
*/
public NativeArray() {
dense = null;
this.length = 0;
}
public NativeArray(long length) {
int intLength = (int) length;
if (intLength == length && intLength > 0) {
if (intLength > maximumDenseLength)
intLength = maximumDenseLength;
dense = new Object[intLength];
for (int i=0; i < intLength; i++)
dense[i] = NOT_FOUND;
}
this.length = length;
}
public NativeArray(Object[] array) {
dense = array;
this.length = array.length;
}
public static void finishInit(Scriptable scope, FunctionObject ctor,
Scriptable proto)
{
// Set some method length values.
// See comment for NativeString.finishInit()
String[] specialLengthNames = { "reverse",
"toString",
};
short[] specialLengthValues = { 0,
0,
};
for (int i=0; i < specialLengthNames.length; i++) {
Object obj = proto.get(specialLengthNames[i], proto);
((FunctionObject) obj).setLength(specialLengthValues[i]);
}
}
public String getClassName() {
return "Array";
}
public Object get(int index, Scriptable start) {
if (dense != null && 0 <= index && index < dense.length)
return dense[index];
return super.get(index, start);
}
public boolean has(int index, Scriptable start) {
if (dense != null && 0 <= index && index < dense.length)
return dense[index] != NOT_FOUND;
return super.has(index, start);
}
public void put(String id, Scriptable start, Object value) {
// only set the array length if given an array index (ECMA 15.4.0)
// try to get an array index from id
double d = ScriptRuntime.toNumber(id);
if (ScriptRuntime.toUint32(d) == d &&
ScriptRuntime.numberToString(d, 10).equals(id) &&
this.length <= d && d != 4294967295.0)
{
this.length = (long)d + 1;
}
super.put(id, start, value);
}
public void put(int index, Scriptable start, Object value) {
// only set the array length if given an array index (ECMA 15.4.0)
if (this.length <= index) {
// avoid overflowing index!
this.length = (long)index + 1;
}
if (dense != null && 0 <= index && index < dense.length) {
dense[index] = value;
return;
}
super.put(index, start, value);
}
public void delete(int index) {
if (dense != null && 0 <= index && index < dense.length) {
dense[index] = NOT_FOUND;
return;
}
super.delete(index);
}
public Object[] getIds() {
Object[] superIds = super.getIds();
if (dense == null)
return superIds;
int count = 0;
int last = dense.length;
if (last > length)
last = (int) length;
for (int i=last-1; i >= 0; i--) {
if (dense[i] != NOT_FOUND)
count++;
}
count += superIds.length;
Object[] result = new Object[count];
System.arraycopy(superIds, 0, result, 0, superIds.length);
for (int i=last-1; i >= 0; i--) {
if (dense[i] != NOT_FOUND)
result[--count] = new Integer(i);
}
return result;
}
public Object getDefaultValue(Class hint) {
if (hint == ScriptRuntime.NumberClass) {
Context cx = Context.getContext();
if (cx.getLanguageVersion() == Context.VERSION_1_2)
return new Long(length);
}
return super.getDefaultValue(hint);
}
/**
* See ECMA 15.4.1,2
*/
public static Object jsConstructor(Context cx, Object[] args,
Function ctorObj, boolean inNewExpr)
throws JavaScriptException
{
if (!inNewExpr) {
// FunctionObject.construct will set up parent, proto
return ctorObj.construct(cx, ctorObj.getParentScope(), args);
}
if (args.length == 0)
return new NativeArray();
// Only use 1 arg as first element for version 1.2; for
// any other version (including 1.3) follow ECMA and use it as
// a length.
if (args.length == 1 && args[0] instanceof Number &&
cx.getLanguageVersion() != cx.VERSION_1_2)
{
return new NativeArray(ScriptRuntime.toUint32(args[0]));
}
// initialize array with arguments
return new NativeArray(args);
}
private static final int lengthAttr = ScriptableObject.DONTENUM |
ScriptableObject.PERMANENT;
public long jsGet_length() {
return length;
}
public void jsSet_length(Object val) {
/* XXX do we satisfy this?
* 15.4.5.1 [[Put]](P, V):
* 1. Call the [[CanPut]] method of A with name P.
* 2. If Result(1) is false, return.
* ?
*/
long longVal = ScriptRuntime.toUint32(val);
if (longVal < length) {
// remove all properties between longVal and length
if (length - longVal > 0x1000) {
// assume that the representation is sparse
Object[] e = getIds(); // will only find in object itself
for (int i=0; i < e.length; i++) {
if (e[i] instanceof String) {
// > MAXINT will appear as string
String id = (String) e[i];
double d = ScriptRuntime.toNumber(id);
if (d == d && d < length)
delete(id);
continue;
}
int index = ((Number) e[i]).intValue();
if (index >= longVal)
delete(index);
}
} else {
// assume a dense representation
for (long i=longVal; i < length; i++) {
// only delete if defined in the object itself
if (hasElem(this, i))
ScriptRuntime.delete(this, new Long(i));
}
}
}
length = longVal;
}
/* Support for generic Array-ish objects. Most of the Array
* functions try to be generic; anything that has a length
* property is assumed to be an array. hasLengthProperty is
* needed in addition to getLengthProperty, because
* getLengthProperty always succeeds - tries to convert strings, etc.
*/
static double getLengthProperty(Scriptable obj) {
// These will both give numeric lengths within Uint32 range.
if (obj instanceof NativeString)
return (double)((NativeString)obj).jsGet_length();
if (obj instanceof NativeArray)
return (double)((NativeArray)obj).jsGet_length();
return ScriptRuntime.toUint32(ScriptRuntime
.getProp(obj, "length", obj));
}
static boolean hasLengthProperty(Object obj) {
if (!(obj instanceof Scriptable) || obj == Context.getUndefinedValue())
return false;
if (obj instanceof NativeString || obj instanceof NativeArray)
return true;
Scriptable sobj = (Scriptable)obj;
// XXX some confusion as to whether or not to walk to get the length
// property. Pending review of js/[new ecma submission] treatment
// of 'arrayness'.
Object property = ScriptRuntime.getProp(sobj, "length", sobj);
return property instanceof Number;
}
/* Utility functions to encapsulate index > Integer.MAX_VALUE
* handling. Also avoids unnecessary object creation that would
* be necessary to use the general ScriptRuntime.get/setElem
* functions... though this is probably premature optimization.
*/
private static boolean hasElem(Scriptable target, long index) {
return index > Integer.MAX_VALUE
? target.has(Long.toString(index), target)
: target.has((int)index, target);
}
private static Object getElem(Scriptable target, long index) {
if (index > Integer.MAX_VALUE) {
String id = Long.toString(index);
return ScriptRuntime.getElem(target, id, target);
} else {
return ScriptRuntime.getElem(target, (int)index);
}
}
private static void setElem(Scriptable target, long index, Object value) {
if (index > Integer.MAX_VALUE) {
String id = Long.toString(index);
ScriptRuntime.setElem(target, id, value, target);
} else {
ScriptRuntime.setElem(target, (int)index, value);
}
}
public static String jsFunction_toString(Context cx, Scriptable thisObj,
Object[] args, Function funObj)
{
return toStringHelper(cx, thisObj,
cx.getLanguageVersion() == cx.VERSION_1_2);
}
private static String toStringHelper(Context cx, Scriptable thisObj,
boolean toSource)
{
/* It's probably redundant to handle long lengths in this
* function; StringBuffers are limited to 2^31 in java.
*/
long length = (long)getLengthProperty(thisObj);
StringBuffer result = new StringBuffer();
if (cx.iterating == null)
cx.iterating = new Hashtable(31);
boolean iterating = cx.iterating.get(thisObj) == Boolean.TRUE;
// whether to return '4,unquoted,5' or '[4, "quoted", 5]'
String separator;
if (toSource) {
result.append("[");
separator = ", ";
} else {
separator = ",";
}
boolean haslast = false;
long i = 0;
if (!iterating) {
for (i = 0; i < length; i++) {
if (i > 0)
result.append(separator);
Object elem = getElem(thisObj, i);
if (elem == null || elem == Undefined.instance) {
haslast = false;
continue;
}
haslast = true;
if (elem instanceof String) {
if (toSource) {
result.append("\"");
result.append(ScriptRuntime.escapeString
(ScriptRuntime.toString(elem)));
result.append("\"");
} else {
result.append(ScriptRuntime.toString(elem));
}
} else {
/* wrap changes to cx.iterating in a try/finally
* so that the reference always gets removed, and
* we don't leak memory. Good place for weak
* references, if we had them. */
try {
// stop recursion.
cx.iterating.put(thisObj, Boolean.TRUE);
result.append(ScriptRuntime.toString(elem));
} finally {
cx.iterating.remove(thisObj);
}
}
}
}
if (toSource) {
//for [,,].length behavior; we want toString to be symmetric.
if (!haslast && i > 0)
result.append(", ]");
else
result.append("]");
}
return result.toString();
}
/**
* See ECMA 15.4.4.3
*/
public static String jsFunction_join(Context cx, Scriptable thisObj,
Object[] args, Function funObj)
{
StringBuffer result = new StringBuffer();
String separator;
double length = getLengthProperty(thisObj);
// if no args, use "," as separator
if (args.length < 1) {
separator = ",";
} else {
separator = ScriptRuntime.toString(args[0]);
}
for (long i=0; i < length; i++) {
if (i > 0)
result.append(separator);
Object temp = getElem(thisObj, i);
if (temp == null || temp == Undefined.instance)
continue;
result.append(ScriptRuntime.toString(temp));
}
return result.toString();
}
/**
* See ECMA 15.4.4.4
*/
public static Scriptable jsFunction_reverse(Context cx, Scriptable thisObj,
Object[] args, Function funObj)
{
long len = (long)getLengthProperty(thisObj);
long half = len / 2;
for(long i=0; i < half; i++) {
long j = len - i - 1;
Object temp1 = getElem(thisObj, i);
Object temp2 = getElem(thisObj, j);
setElem(thisObj, i, temp2);
setElem(thisObj, j, temp1);
}
return thisObj;
}
/**
* See ECMA 15.4.4.5
*/
public static Scriptable jsFunction_sort(Context cx, Scriptable thisObj,
Object[] args, Function funObj)
throws JavaScriptException
{
long length = (long)getLengthProperty(thisObj);
Object compare;
if (args.length > 0 && Undefined.instance != args[0])
// sort with given compare function
compare = args[0];
else
// sort with default compare
compare = null;
// OPT: Would it make sense to use the extended sort for very small
// arrays?
// Should we use the extended sort function, or the faster one?
if (length >= Integer.MAX_VALUE) {
qsort_extended(cx, compare, thisObj, 0, length - 1);
} else {
// copy the JS array into a working array, so it can be
// sorted cheaply.
Object[] working = new Object[(int)length];
for (int i=0; i<length; i++) {
working[i] = getElem(thisObj, i);
}
qsort(cx, compare, working, 0, (int)length - 1);
// copy the working array back into thisObj
for (int i=0; i<length; i++) {
setElem(thisObj, i, working[i]);
}
}
return thisObj;
}
private static double qsortCompare(Context cx, Object jsCompare, Object x,
Object y)
throws JavaScriptException
{
Object undef = Undefined.instance;
// sort undefined to end
if (undef == x || undef == y) {
if (undef != x)
return -1;
if (undef != y)
return 1;
return 0;
}
if (jsCompare == null) {
// if no compare function supplied, sort lexicographically
String a = ScriptRuntime.toString(x);
String b = ScriptRuntime.toString(y);
return a.compareTo(b);
} else {
// assemble args and call supplied JS compare function
// OPT: put this argument creation in the caller and reuse it.
// XXX what to do when compare function returns NaN? ECMA states
// that it's then not a 'consistent compararison function'... but
// then what do we do? Back out and start over with the generic
// compare function when we see a NaN? Throw an error?
Object[] args = {x, y};
// return ScriptRuntime.toNumber(ScriptRuntime.call(jsCompare, null,
// args));
// for now, just ignore it:
double d = ScriptRuntime.
toNumber(ScriptRuntime.call(cx, jsCompare, null, args));
return (d == d) ? d : 0;
}
}
private static void qsort(Context cx, Object jsCompare, Object[] working,
int lo, int hi)
throws JavaScriptException
{
Object pivot;
int i, j;
int a, b;
while (lo < hi) {
i = lo;
j = hi;
a = i;
pivot = working[a];
while (i < j) {
for(;;) {
b = j;
if (qsortCompare(cx, jsCompare, working[j], pivot) <= 0)
break;
j--;
}
working[a] = working[b];
while (i < j && qsortCompare(cx, jsCompare, working[a],
pivot) <= 0)
{
i++;
a = i;
}
working[b] = working[a];
}
working[a] = pivot;
if (i - lo < hi - i) {
qsort(cx, jsCompare, working, lo, i - 1);
lo = i + 1;
} else {
qsort(cx, jsCompare, working, i + 1, hi);
hi = i - 1;
}
}
}
// A version that knows about long indices and doesn't use
// a working array. Probably will never be used.
private static void qsort_extended(Context cx, Object jsCompare,
Scriptable target, long lo, long hi)
throws JavaScriptException
{
Object pivot;
long i, j;
long a, b;
while (lo < hi) {
i = lo;
j = hi;
a = i;
pivot = getElem(target, a);
while (i < j) {
for(;;) {
b = j;
if (qsortCompare(cx, jsCompare, getElem(target, j),
pivot) <= 0)
break;
j--;
}
setElem(target, a, getElem(target, b));
while (i < j && qsortCompare(cx, jsCompare,
getElem(target, a), pivot) <= 0)
{
i++;
a = i;
}
setElem(target, b, getElem(target, a));
}
setElem(target, a, pivot);
if (i - lo < hi - i) {
qsort_extended(cx, jsCompare, target, lo, i - 1);
lo = i + 1;
} else {
qsort_extended(cx, jsCompare, target, i + 1, hi);
hi = i - 1;
}
}
}
/**
* Non-ECMA methods.
*/
public static Object jsFunction_push(Context cx, Scriptable thisObj,
Object[] args, Function funObj)
{
double length = getLengthProperty(thisObj);
for (int i = 0; i < args.length; i++) {
setElem(thisObj, (long)length + i, args[i]);
}
length += args.length;
ScriptRuntime.setProp(thisObj, "length", new Double(length), thisObj);
/*
* If JS1.2, follow Perl4 by returning the last thing pushed.
* Otherwise, return the new array length.
*/
if (cx.getLanguageVersion() == Context.VERSION_1_2)
// if JS1.2 && no arguments, return undefined.
return args.length == 0
? Context.getUndefinedValue()
: args[args.length - 1];
else
return new Long((long)length);
}
public static Object jsFunction_pop(Context cx, Scriptable thisObj,
Object[] args, Function funObj)
{
Object result;
double length = getLengthProperty(thisObj);
if (length > 0) {
length--;
// Get the to-be-deleted property's value.
result = getElem(thisObj, (long)length);
// We don't need to delete the last property, because
// setLength does that for us.
} else {
result = Context.getUndefinedValue();
}
// necessary to match js even when length < 0; js pop will give a
// length property to any target it is called on.
ScriptRuntime.setProp(thisObj, "length", new Double(length), thisObj);
return result;
}
public static Object jsFunction_shift(Context cx, Scriptable thisObj,
Object[] args, Function funOjb)
{
Object result;
double length = getLengthProperty(thisObj);
if (length > 0) {
long i = 0;
length--;
// Get the to-be-deleted property's value.
result = getElem(thisObj, i);
/*
* Slide down the array above the first element. Leave i
* set to point to the last element.
*/
if (length > 0) {
for (i = 1; i <= length; i++) {
Object temp = getElem(thisObj, i);
setElem(thisObj, i - 1, temp);
}
}
// We don't need to delete the last property, because
// setLength does that for us.
} else {
result = Context.getUndefinedValue();
}
ScriptRuntime.setProp(thisObj, "length", new Double(length), thisObj);
return result;
}
public static Object jsFunction_unshift(Context cx, Scriptable thisObj,
Object[] args, Function funOjb)
{
Object result;
double length = (double)getLengthProperty(thisObj);
int argc = args.length;
if (args.length > 0) {
/* Slide up the array to make room for args at the bottom */
if (length > 0) {
for (long last = (long)length - 1; last >= 0; last--) {
Object temp = getElem(thisObj, last);
setElem(thisObj, last + argc, temp);
}
}
/* Copy from argv to the bottom of the array. */
for (int i = 0; i < args.length; i++) {
setElem(thisObj, i, args[i]);
}
/* Follow Perl by returning the new array length. */
length += args.length;
ScriptRuntime.setProp(thisObj, "length",
new Double(length), thisObj);
}
return new Long((long)length);
}
public static Object jsFunction_splice(Context cx, Scriptable thisObj,
Object[] args, Function funObj)
{
/* create an empty Array to return. */
Scriptable scope = getTopLevelScope(funObj);
Object result = ScriptRuntime.newObject(cx, scope, "Array", null);
int argc = args.length;
if (argc == 0)
return result;
double length = getLengthProperty(thisObj);
/* Convert the first argument into a starting index. */
double begin = ScriptRuntime.toInteger(args[0]);
double end;
double delta;
double count;
if (begin < 0) {
begin += length;
if (begin < 0)
begin = 0;
} else if (begin > length) {
begin = length;
}
argc--;
/* Convert the second argument from a count into a fencepost index. */
delta = length - begin;
if (args.length == 1) {
count = delta;
end = length;
} else {
count = ScriptRuntime.toInteger(args[1]);
if (count < 0)
count = 0;
else if (count > delta)
count = delta;
end = begin + count;
argc--;
}
long lbegin = (long)begin;
long lend = (long)end;
/* If there are elements to remove, put them into the return value. */
if (count > 0) {
if (count == 1
&& (cx.getLanguageVersion() == Context.VERSION_1_2))
{
/*
* JS lacks "list context", whereby in Perl one turns the
* single scalar that's spliced out into an array just by
* assigning it to @single instead of $single, or by using it
* as Perl push's first argument, for instance.
*
* JS1.2 emulated Perl too closely and returned a non-Array for
* the single-splice-out case, requiring callers to test and
* wrap in [] if necessary. So JS1.3, default, and other
* versions all return an array of length 1 for uniformity.
*/
result = getElem(thisObj, lbegin);
} else {
for (long last = lbegin; last < lend; last++) {
Scriptable resultArray = (Scriptable)result;
Object temp = getElem(thisObj, last);
setElem(resultArray, last - lbegin, temp);
}
}
} else if (count == 0
&& cx.getLanguageVersion() == Context.VERSION_1_2)
{
/* Emulate C JS1.2; if no elements are removed, return undefined. */
result = Context.getUndefinedValue();
}
/* Find the direction (up or down) to copy and make way for argv. */
delta = argc - count;
if (delta > 0) {
for (long last = (long)length - 1; last >= lend; last--) {
Object temp = getElem(thisObj, last);
setElem(thisObj, last + (long)delta, temp);
}
} else if (delta < 0) {
for (long last = lend; last < length; last++) {
Object temp = getElem(thisObj, last);
setElem(thisObj, last + (long)delta, temp);
}
}
/* Copy from argv into the hole to complete the splice. */
int argoffset = args.length - argc;
for (int i = 0; i < argc; i++) {
setElem(thisObj, lbegin + i, args[i + argoffset]);
}
/* Update length in case we deleted elements from the end. */
ScriptRuntime.setProp(thisObj, "length",
new Double(length + delta), thisObj);
return result;
}
/*
* Python-esque sequence operations.
*/
public static Scriptable jsFunction_concat(Context cx, Scriptable thisObj,
Object[] args, Function funObj)
{
/* Concat tries to keep the definition of an array as general
* as possible; if it finds that an object has a numeric
* 'length' property, then it treats that object as an array.
* This treats string atoms and string objects differently; as
* string objects have a length property and are accessible by
* index, they get exploded into arrays when added, while
* atomic strings are just added as strings.
*/
// create an empty Array to return.
Scriptable scope = getTopLevelScope(funObj);
Scriptable result = ScriptRuntime.newObject(cx, scope, "Array", null);
double length;
long slot = 0;
/* Put the target in the result array; only add it as an array
* if it looks like one.
*/
if (hasLengthProperty(thisObj)) {
length = getLengthProperty(thisObj);
// Copy from the target object into the result
for (slot = 0; slot < length; slot++) {
Object temp = getElem(thisObj, slot);
setElem(result, slot, temp);
}
} else {
setElem(result, slot++, thisObj);
}
/* Copy from the arguments into the result. If any argument
* has a numeric length property, treat it as an array and add
* elements separately; otherwise, just copy the argument.
*/
for (int i = 0; i < args.length; i++) {
if (hasLengthProperty(args[i])) {
// hasLengthProperty => instanceOf Scriptable.
Scriptable arg = (Scriptable)args[i];
length = getLengthProperty(arg);
for (long j = 0; j < length; j++, slot++) {
Object temp = getElem(arg, j);
setElem(result, slot, temp);
}
} else {
setElem(result, slot++, args[i]);
}
}
return result;
}
public static Scriptable jsFunction_slice(Context cx, Scriptable thisObj,
Object[] args, Function funObj)
{
Scriptable scope = getTopLevelScope(funObj);
Scriptable result = ScriptRuntime.newObject(cx, scope, "Array", null);
double length = getLengthProperty(thisObj);
double begin = 0;
double end = length;
if (args.length > 0) {
begin = ScriptRuntime.toInteger(args[0]);
if (begin < 0) {
begin += length;
if (begin < 0)
begin = 0;
} else if (begin > length) {
begin = length;
}
if (args.length > 1) {
end = ScriptRuntime.toInteger(args[1]);
if (end < 0) {
end += length;
if (end < 0)
end = 0;
} else if (end > length) {
end = length;
}
}
}
long lbegin = (long)begin;
long lend = (long)end;
for (long slot = lbegin; slot < lend; slot++) {
Object temp = getElem(thisObj, slot);
setElem(result, slot - lbegin, temp);
}
return result;
}
private long length;
private Object[] dense;
private static final int maximumDenseLength = 10000;
}

View File

@@ -1,74 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
package org.mozilla.javascript;
/**
* This class implements the Boolean native object.
* See ECMA 15.6.
* @author Norris Boyd
*/
public class NativeBoolean extends ScriptableObject {
/**
* Zero-parameter constructor: just used to create Boolean.prototype
*/
public NativeBoolean() {
}
public NativeBoolean(boolean b) {
booleanValue = b;
}
public String getClassName() {
return "Boolean";
}
public Object getDefaultValue(Class typeHint) {
// This is actually non-ECMA, but will be proposed
// as a change in round 2.
if (typeHint == ScriptRuntime.BooleanClass)
return booleanValue ? Boolean.TRUE : Boolean.FALSE;
return super.getDefaultValue(typeHint);
}
public static Object jsConstructor(Context cx, Object[] args,
Function ctorObj, boolean inNewExpr)
{
boolean b = args.length >= 1
? ScriptRuntime.toBoolean(args[0])
: false;
if (inNewExpr) {
// new Boolean(val) creates a new boolean object.
return new NativeBoolean(b);
}
// Boolean(val) converts val to a boolean.
return b ? Boolean.TRUE : Boolean.FALSE;
}
public String jsFunction_toString() {
return booleanValue ? "true" : "false";
}
public boolean jsFunction_valueOf() {
return booleanValue;
}
private boolean booleanValue;
}

View File

@@ -1,128 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
package org.mozilla.javascript;
/**
* This class implements the activation object.
*
* See ECMA 10.1.6
*
* @see org.mozilla.javascript.Arguments
* @author Norris Boyd
*/
public final class NativeCall extends ScriptableObject {
NativeCall(Context cx, Scriptable scope, NativeFunction funObj,
Scriptable thisObj, Object[] args)
{
this(cx, scope, funObj, thisObj);
this.originalArgs = args;
// initialize references to nested functions
NativeFunction[] fns = funObj.nestedFunctions;
if (fns != null) {
for (int i=0; i < fns.length; i++) {
NativeFunction f = fns[i];
if (f.names != null)
super.put(f.names[0], this, f);
}
}
// initialize values of arguments
String[] names = funObj.names;
if (names != null) {
for (int i=0; i < funObj.argCount; i++) {
Object val = i < args.length ? args[i]
: Undefined.instance;
super.put(names[i+1], this, val);
}
}
// initialize "arguments" property
super.put("arguments", this, new Arguments(this));
}
NativeCall(Context cx, Scriptable scope, NativeFunction funObj,
Scriptable thisObj)
{
this.funObj = funObj;
this.thisObj = thisObj;
setParentScope(scope);
// leave prototype null
// save current activation
this.caller = cx.currentActivation;
cx.currentActivation = this;
}
// Needed in order to use this class with ScriptableObject.defineClass
public NativeCall() {
}
public String getClassName() {
return "Call";
}
public static Object jsConstructor(Context cx, Object[] args,
Function ctorObj, boolean inNewExpr)
{
if (!inNewExpr) {
Object[] errArgs = { "Call" };
throw Context.reportRuntimeError(Context.getMessage
("msg.only.from.new", errArgs));
}
ScriptRuntime.checkDeprecated(cx, "Call");
NativeCall result = new NativeCall();
result.setPrototype(getObjectPrototype(ctorObj));
return result;
}
NativeCall getActivation(NativeFunction f) {
NativeCall x = this;
do {
if (x.funObj == f)
return x;
x = x.caller;
} while (x != null);
return null;
}
public NativeFunction getFunctionObject() {
return funObj;
}
public Object[] getOriginalArguments() {
return originalArgs;
}
public NativeCall getCaller() {
return caller;
}
public Scriptable getThisObj() {
return thisObj;
}
NativeCall caller;
NativeFunction funObj;
Scriptable thisObj;
Object[] originalArgs;
public int debugPC;
}

View File

@@ -1,103 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
package org.mozilla.javascript;
/**
* This class implements the closure object.
*
* @author Norris Boyd
*/
public class NativeClosure extends ScriptableObject implements Function {
public NativeClosure() {
}
public NativeClosure(Context cx, Scriptable scope, NativeFunction f) {
setPrototype(f);
setParentScope(scope);
String name = f.names != null ? f.names[0] : "";
if (name != null && name.length() > 0) {
scope.put(name, scope, scope.getParentScope() == null
? (Object) f : this);
}
}
public String getClassName() {
return "Closure";
}
public Object getDefaultValue(Class typeHint) {
if (typeHint == ScriptRuntime.FunctionClass)
return prototype;
return super.getDefaultValue(typeHint);
}
public Object call(Context cx, Scriptable scope, Scriptable thisObj,
Object[] args)
throws JavaScriptException
{
Function proto = checkProto();
return proto.call(cx, getParentScope(), thisObj, args);
}
public Scriptable construct(Context cx, Scriptable scope, Object[] args)
throws JavaScriptException
{
Function proto = checkProto();
return proto.construct(cx, getParentScope(), args);
}
public static Object jsConstructor(Context cx, Object[] args,
Function ctorObj, boolean inNewExpr)
{
Object[] msgArgs = { "Closure" };
throw Context.reportRuntimeError(
Context.getMessage("msg.cant.call.indirect", msgArgs));
}
public static Object newClosureSpecial(Context cx, Scriptable varObj,
Object[] args, Function ctorObj)
{
ScriptRuntime.checkDeprecated(cx, "Closure");
NativeFunction f = args.length > 0 &&
args[0] instanceof NativeFunction
? (NativeFunction) args[0]
: null;
NativeClosure result = f != null
? new NativeClosure(cx, varObj, f)
: new NativeClosure();
Scriptable scope = getTopLevelScope(ctorObj);
result.setPrototype(args.length == 0
? getObjectPrototype(scope)
: ScriptRuntime.toObject(scope, args[0]));
result.setParentScope(varObj);
return result;
}
private Function checkProto() {
Scriptable proto = getPrototype();
if (!(proto instanceof Function)) {
throw Context.reportRuntimeError(Context.getMessage
("msg.closure.proto", null));
}
return (Function) proto;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,403 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
package org.mozilla.javascript;
import java.io.StringReader;
import java.io.IOException;
/**
* This class implements the global native object (function and value
* properties only).
*
* See ECMA 15.1.[12].
*
* @author Mike Shaver
*/
public class NativeGlobal {
public static void init(Scriptable scope)
throws PropertyException
{
String names[] = { "eval",
"parseInt",
"parseFloat",
"escape",
"unescape",
"isNaN",
"isFinite"
};
// We can downcast here because Context.initStandardObjects
// takes a ScriptableObject scope.
ScriptableObject global = (ScriptableObject) scope;
global.defineFunctionProperties(names, NativeGlobal.class,
ScriptableObject.DONTENUM);
global.defineProperty("NaN", ScriptRuntime.NaNobj,
ScriptableObject.DONTENUM);
global.defineProperty("Infinity", new Double(Double.POSITIVE_INFINITY),
ScriptableObject.DONTENUM);
global.defineProperty("undefined", Undefined.instance,
ScriptableObject.DONTENUM);
}
/**
* The global method parseInt, as per ECMA-262 15.1.2.2.
*/
public static Object parseInt(String s, int radix) {
int len = s.length();
if (len == 0)
return ScriptRuntime.NaNobj;
boolean negative = false;
int start = 0;
char c;
do {
c = s.charAt(start);
if (!Character.isWhitespace(c))
break;
start++;
} while (start < len);
if (c == '+' || (negative = (c == '-')))
start++;
final int NO_RADIX = -1;
if (radix == 0) {
radix = NO_RADIX;
} else if (radix < 2 || radix > 36) {
return ScriptRuntime.NaNobj;
} else if (radix == 16 && len - start > 1 &&
s.charAt(start) == '0')
{
c = s.charAt(start+1);
if (c == 'x' || c == 'X')
start += 2;
}
if (radix == NO_RADIX) {
radix = 10;
if (len - start > 1 && s.charAt(start) == '0') {
c = s.charAt(start+1);
if (c == 'x' || c == 'X') {
radix = 16;
start += 2;
} else if (c != '.') {
radix = 8;
start++;
}
}
}
double d = ScriptRuntime.stringToNumber(s, start, radix);
return new Double(negative ? -d : d);
}
/**
* The global method parseFloat, as per ECMA-262 15.1.2.3.
*
* @param cx unused
* @param thisObj unused
* @param args the arguments to parseFloat, ignoring args[>=1]
* @param funObj unused
*/
public static Object parseFloat(Context cx, Scriptable thisObj,
Object[] args, Function funObj)
{
if (args.length < 1)
return ScriptRuntime.NaNobj;
String s = ScriptRuntime.toString(args[0]);
int len = s.length();
if (len == 0)
return ScriptRuntime.NaNobj;
int i;
char c;
// Scan forward to the first digit or .
for (i=0; TokenStream.isJSSpace(c = s.charAt(i)) && i+1 < len; i++)
/* empty */
;
int start = i;
if (c == '+' || c == '-')
c = s.charAt(++i);
if (c == 'I') {
// check for "Infinity"
double d;
if (i+8 <= len && s.substring(i, i+8).equals("Infinity"))
d = s.charAt(start) == '-' ? Double.NEGATIVE_INFINITY
: Double.POSITIVE_INFINITY;
else
return ScriptRuntime.NaNobj;
return new Double(d);
}
// Find the end of the legal bit
int decimal = -1;
int exponent = -1;
for (; i < len; i++) {
switch (s.charAt(i)) {
case '.':
if (decimal != -1) // Only allow a single decimal point.
break;
decimal = i;
continue;
case 'e':
case 'E':
if (exponent != -1)
break;
exponent = i;
continue;
case '+':
case '-':
// Only allow '+' or '-' after 'e' or 'E'
if (exponent != i-1)
break;
continue;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
continue;
default:
break;
}
break;
}
s = s.substring(start, i);
try {
return Double.valueOf(s);
}
catch (NumberFormatException ex) {
return ScriptRuntime.NaNobj;
}
}
/**
* The global method escape, as per ECMA-262 15.1.2.4.
* Includes code for the 'mask' argument supported by the C escape
* method, which used to be part of the browser imbedding. Blame
* for the strange constant names should be directed there.
*/
private static int
URL_XALPHAS = 1,
URL_XPALPHAS = 2,
URL_PATH = 4;
public static Object escape(Context cx, Scriptable thisObj,
Object[] args, Function funObj)
{
char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
if (args.length < 1)
args = ScriptRuntime.padArguments(args, 1);
String s = ScriptRuntime.toString(args[0]);
int mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH;
if (args.length > 1) { // the 'mask' argument. Non-ECMA.
double d = ScriptRuntime.toNumber(args[1]);
if (d != d || ((mask = (int) d) != d) ||
0 != (mask & ~(URL_XALPHAS | URL_XPALPHAS | URL_PATH)))
{
String message = Context.getMessage
("msg.bad.esc.mask", null);
cx.reportError(message);
// do the ecma thing, in case reportError returns.
mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH;
}
}
StringBuffer R = new StringBuffer();
for (int k = 0; k < s.length(); k++) {
char c = s.charAt(k);
if (mask != 0 &&
((c >= '0' && c <= '9') ||
(c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z') ||
c == '@' || c == '*' || c == '_' ||
c == '-' || c == '.' ||
((c == '/' || c == '+') && mask > 3)))
R.append(c);
else if (c < 256) {
if (c == ' ' && mask == URL_XPALPHAS) {
R.append('+');
} else {
R.append('%');
R.append(digits[c >> 4]);
R.append(digits[c & 0xF]);
}
} else {
R.append('%');
R.append('u');
R.append(digits[c >> 12]);
R.append(digits[(c & 0xF00) >> 8]);
R.append(digits[(c & 0xF0) >> 4]);
R.append(digits[c & 0xF]);
}
}
return R.toString();
}
/**
* The global unescape method, as per ECMA-262 15.1.2.5.
*/
public static Object unescape(Context cx, Scriptable thisObj,
Object[] args, Function funObj)
{
if (args.length < 1)
args = ScriptRuntime.padArguments(args, 1);
String s = ScriptRuntime.toString(args[0]);
StringBuffer R = new StringBuffer();
stringIter: for (int k = 0; k < s.length(); k++) {
char c = s.charAt(k);
if (c != '%' || k == s.length() -1) {
R.append(c);
continue;
}
String hex;
int end, start;
if (s.charAt(k+1) == 'u') {
start = k+2;
end = k+6;
} else {
start = k+1;
end = k+3;
}
if (end > s.length()) {
R.append('%');
continue;
}
hex = s.substring(start, end);
for (int i = 0; i < hex.length(); i++)
if (!TokenStream.isXDigit(hex.charAt(i))) {
R.append('%');
continue stringIter;
}
k = end - 1;
R.append((new Character((char) Integer.valueOf(hex, 16).intValue())));
}
return R.toString();
}
/**
* The global method isNaN, as per ECMA-262 15.1.2.6.
*/
public static Object isNaN(Context cx, Scriptable thisObj,
Object[] args, Function funObj)
{
if (args.length < 1)
return Boolean.TRUE;
double d = ScriptRuntime.toNumber(args[0]);
return (d != d) ? Boolean.TRUE : Boolean.FALSE;
}
public static Object isFinite(Context cx, Scriptable thisObj,
Object[] args, Function funObj)
{
if (args.length < 1)
return Boolean.FALSE;
double d = ScriptRuntime.toNumber(args[0]);
return (d != d || d == Double.POSITIVE_INFINITY ||
d == Double.NEGATIVE_INFINITY)
? Boolean.FALSE
: Boolean.TRUE;
}
public static Object eval(Context cx, Scriptable thisObj,
Object[] args, Function funObj)
throws JavaScriptException
{
Object[] msgArgs = { "eval" };
throw Context.reportRuntimeError(
Context.getMessage("msg.cant.call.indirect", msgArgs));
}
/**
* The eval function property of the global object.
*
* See ECMA 15.1.2.1
*/
public static Object evalSpecial(Context cx, Scriptable scope,
Object thisArg, Object[] args,
String filename, int lineNumber)
throws JavaScriptException
{
if (args.length < 1)
return Undefined.instance;
Object x = args[0];
if (!(x instanceof String)) {
String message = Context.getMessage("msg.eval.nonstring", null);
Context.reportWarning(message);
return x;
}
int[] linep = { lineNumber };
if (filename == null) {
filename = Context.getSourcePositionFromStack(linep);
if (filename == null) {
filename = "<eval'ed string>";
linep[0] = 1;
}
}
try {
StringReader in = new StringReader((String) x);
Object securityDomain = cx.getSecurityDomainForStackDepth(3);
// Compile the reader with opt level of -1 to force interpreter
// mode.
int oldOptLevel = cx.getOptimizationLevel();
cx.setOptimizationLevel(-1);
Script script = cx.compileReader(null, in, filename, linep[0],
securityDomain);
cx.setOptimizationLevel(oldOptLevel);
// if the compile fails, an error has been reported by the
// compiler, but we need to stop execution to avoid
// infinite looping on while(true) { eval('foo bar') } -
// so we throw an EvaluatorException.
if (script == null) {
String message = Context.getMessage("msg.syntax", null);
throw new EvaluatorException(message);
}
InterpretedScript is = (InterpretedScript) script;
Object result = is.call(cx, scope, (Scriptable) thisArg, null);
return result;
}
catch (IOException ioe) {
// should never happen since we just made the Reader from a String
throw new RuntimeException("unexpected io exception");
}
}
}

View File

@@ -1,129 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
package org.mozilla.javascript;
import java.lang.reflect.Array;
/**
* This class reflects Java arrays into the JavaScript environment.
*
* @author Mike Shaver
* @see NativeJavaClass
* @see NativeJavaObject
* @see NativeJavaPackage
*/
public class NativeJavaArray extends NativeJavaObject {
public String getClassName() {
return "JavaArray";
}
public static NativeJavaArray wrap(Scriptable scope, Object array) {
return new NativeJavaArray(scope, array);
}
public Object unwrap() {
return array;
}
public NativeJavaArray(Scriptable scope, Object array) {
super(scope, null, ScriptRuntime.ObjectClass);
Class cl = array.getClass();
if (!cl.isArray()) {
throw new RuntimeException("Array expected");
}
this.array = array;
this.length = Array.getLength(array);
this.cls = cl.getComponentType();
}
public boolean has(String id, Scriptable start) {
return id.equals("length") || super.has(id, start);
}
public boolean has(int index, Scriptable start) {
return 0 <= index && index < length;
}
public Object get(String id, Scriptable start) {
if (id.equals("length"))
return new Integer(length);
return super.get(id, start);
}
public Object get(int index, Scriptable start) {
if (0 <= index && index < length)
return NativeJavaObject.wrap(this, Array.get(array, index), cls);
return Undefined.instance;
}
public void put(String id, Scriptable start, Object value) {
// Ignore assignments to "length"--it's readonly.
if (!id.equals("length"))
super.put(id, start, value);
}
public void put(int index, Scriptable start, Object value) {
if (0 <= index && index < length) {
Array.set(array, index, NativeJavaObject.coerceType(cls, value));
return;
}
super.put(index, start, value);
}
public Object getDefaultValue(Class hint) {
if (hint == null || hint == ScriptRuntime.StringClass)
return array.toString();
if (hint == ScriptRuntime.BooleanClass)
return Boolean.TRUE;
if (hint == ScriptRuntime.NumberClass)
return ScriptRuntime.NaNobj;
return this;
}
public Object[] getIds() {
Object[] result = new Object[length];
int i = length;
while (--i >= 0)
result[i] = new Integer(i);
return result;
}
public boolean hasInstance(Scriptable value) {
if (!(value instanceof NativeJavaObject))
return false;
Object instance = ((NativeJavaObject)value).unwrap();
return cls.isInstance(instance);
}
public Scriptable getPrototype() {
if (prototype == null) {
prototype =
ScriptableObject.getClassPrototype(this.getParentScope(),
"Array");
}
return prototype;
}
Object array;
int length;
Class cls;
Scriptable prototype;
}

View File

@@ -1,243 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
package org.mozilla.javascript;
import java.lang.reflect.*;
import java.util.Hashtable;
/**
* This class reflects Java classes into the JavaScript environment, mainly
* for constructors and static members. We lazily reflect properties,
* and currently do not guarantee that a single j.l.Class is only
* reflected once into the JS environment, although we should.
* The only known case where multiple reflections
* are possible occurs when a j.l.Class is wrapped as part of a
* method return or property access, rather than by walking the
* Packages/java tree.
*
* @author Mike Shaver
* @see NativeJavaArray
* @see NativeJavaObject
* @see NativeJavaPackage
*/
public class NativeJavaClass extends NativeJavaObject implements Function {
public NativeJavaClass(Scriptable scope, Class cl) {
super(scope, cl, JavaMembers.lookupClass(scope, cl, cl));
fieldAndMethods = members.getFieldAndMethodsObjects(javaObject, false);
}
public String getClassName() {
return "JavaClass";
}
public boolean has(String name, Scriptable start) {
return members.has(name, true);
}
public Object get(String name, Scriptable start) {
// When used as a constructor, ScriptRuntime.newObject() asks
// for our prototype to create an object of the correct type.
// We don't really care what the object is, since we're returning
// one constructed out of whole cloth, so we return null.
if (name.equals("prototype"))
return null;
Object result = Scriptable.NOT_FOUND;
if (fieldAndMethods != null) {
result = fieldAndMethods.get(name);
if (result != null)
return result;
}
if (members.has(name, true)) {
result = members.get(this, name, javaObject, true);
} else {
// experimental: look for nested classes by appending $name to current class' name.
try {
String nestedName = getClassObject().getName() + '$' + name;
Class nestedClass = Class.forName(nestedName);
Scriptable nestedValue = wrap(ScriptableObject.getTopLevelScope(this), nestedClass);
nestedValue.setParentScope(this);
result = nestedValue;
} catch (ClassNotFoundException ex) {
throw members.reportMemberNotFound(name);
}
}
return result;
}
public void put(String name, Scriptable start, Object value) {
members.put(name, javaObject, value, true);
}
public Object[] getIds() {
return members.getIds(true);
}
public Class getClassObject() {
return (Class) super.unwrap();
}
// XXX ??
public static NativeJavaClass wrap(Scriptable scope, Class cls) {
return new NativeJavaClass(scope, cls);
}
public Object getDefaultValue(Class hint) {
if (hint == null || hint == ScriptRuntime.StringClass)
return this.toString();
if (hint == ScriptRuntime.BooleanClass)
return Boolean.TRUE;
if (hint == ScriptRuntime.NumberClass)
return ScriptRuntime.NaNobj;
return this;
}
public Object call(Context cx, Scriptable scope, Scriptable thisObj,
Object[] args)
throws JavaScriptException
{
return construct(cx, scope, args);
}
public Scriptable construct(Context cx, Scriptable scope, Object[] args)
throws JavaScriptException
{
Class classObject = getClassObject();
int modifiers = classObject.getModifiers();
if (! (Modifier.isInterface(modifiers) ||
Modifier.isAbstract(modifiers)))
{
Constructor[] ctors = members.getConstructors();
Member member = NativeJavaMethod.findFunction(ctors, args);
Constructor ctor = (Constructor) member;
if (ctor == null) {
String sig = NativeJavaMethod.scriptSignature(args);
Object errArgs[] = { classObject.getName(), sig };
throw Context.reportRuntimeError(Context.getMessage(
"msg.no.java.ctor", errArgs));
}
// Found the constructor, so try invoking it.
return NativeJavaClass.constructSpecific(cx, scope,
this, ctor, args);
} else {
Scriptable topLevel = ScriptableObject.getTopLevelScope(this);
String msg = "";
try {
// trying to construct an interface; use JavaAdapter to
// construct a new class on the fly that implements this
// interface.
Object v = topLevel.get("JavaAdapter", topLevel);
if (v != NOT_FOUND) {
Function f = (Function) v;
Object[] adapterArgs = { this, args[0] };
return (Scriptable) f.construct(cx, topLevel,
adapterArgs);
}
} catch (Exception ex) {
// fall through to error
msg = ex.getMessage();
}
Object[] errArgs = { msg, classObject.getName() };
throw Context.reportRuntimeError(Context.getMessage
("msg.cant.instantiate",
errArgs));
}
}
public static Scriptable constructSpecific(Context cx,
Scriptable scope,
Scriptable thisObj,
Constructor ctor,
Object[] args)
throws JavaScriptException
{
Scriptable topLevel = ScriptableObject.getTopLevelScope(thisObj);
Class classObject = ctor.getDeclaringClass();
Class[] paramTypes = ctor.getParameterTypes();
for (int i = 0; i < args.length; i++) {
args[i] = NativeJavaObject.coerceType(paramTypes[i], args[i]);
}
try {
// we need to force this to be wrapped, because construct _has_
// to return a scriptable
return
(Scriptable) NativeJavaObject.wrap(topLevel,
ctor.newInstance(args),
classObject);
} catch (InstantiationException instEx) {
Object[] errArgs = { instEx.getMessage(),
classObject.getName() };
throw Context.reportRuntimeError(Context.getMessage
("msg.cant.instantiate",
errArgs));
} catch (IllegalArgumentException argEx) {
String signature = NativeJavaMethod.scriptSignature(args);
String ctorString = ctor.toString();
Object[] errArgs = { argEx.getMessage(),ctorString,signature };
throw Context.reportRuntimeError(Context.getMessage
("msg.bad.ctor.sig",
errArgs));
} catch (InvocationTargetException e) {
throw JavaScriptException.wrapException(scope, e);
} catch (IllegalAccessException accessEx) {
Object[] errArgs = { accessEx.getMessage() };
throw Context.reportRuntimeError(Context.getMessage
("msg.java.internal.private", errArgs));
}
}
public String toString() {
return "[JavaClass " + getClassObject().getName() + "]";
}
/**
* Determines if prototype is a wrapped Java object and performs
* a Java "instanceof".
* Exception: if value is an instance of NativeJavaClass, it isn't
* considered an instance of the Java class; this forestalls any
* name conflicts between java.lang.Class's methods and the
* static methods exposed by a JavaNativeClass.
*/
public boolean hasInstance(Scriptable value) {
if (value instanceof NativeJavaObject &&
!(value instanceof NativeJavaClass)) {
Object instance = ((NativeJavaObject)value).unwrap();
return getClassObject().isInstance(instance);
}
// value wasn't something we understand
return false;
}
private Hashtable fieldAndMethods;
// beard: need a scope for finding top-level prototypes.
private Scriptable parent;
}

View File

@@ -1,81 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
package org.mozilla.javascript;
import java.lang.reflect.*;
/**
* This class reflects a single Java constructor into the JavaScript
* environment. It satisfies a request for an overloaded constructor,
* as introduced in LiveConnect 3.
* All NativeJavaConstructors behave as JSRef `bound' methods, in that they
* always construct the same NativeJavaClass regardless of any reparenting
* that may occur.
*
* @author Frank Mitchell
* @see NativeJavaMethod
* @see NativeJavaPackage
* @see NativeJavaClass
*/
public class NativeJavaConstructor extends NativeFunction implements Function {
public NativeJavaConstructor(Constructor ctor) {
this.constructor = ctor;
names = new String[1];
names[0] = "<init>" + NativeJavaMethod.signature(ctor);
}
public Object call(Context cx, Scriptable scope, Scriptable thisObj,
Object[] args)
throws JavaScriptException
{
// Find a method that matches the types given.
if (constructor == null) {
throw new RuntimeException("No constructor defined for call");
}
// Eliminate useless args[0] and unwrap if required
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof Wrapper) {
args[i] = ((Wrapper)args[i]).unwrap();
}
}
return NativeJavaClass.constructSpecific(cx, scope,
this, constructor, args);
}
/*
public Object getDefaultValue(Class hint) {
return this;
}
*/
public String toString() {
return "[JavaConstructor " + constructor.getName() + "]";
}
Constructor getConstructor() {
return constructor;
}
Constructor constructor;
}

View File

@@ -1,465 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
package org.mozilla.javascript;
import java.lang.reflect.*;
/**
* This class reflects Java methods into the JavaScript environment. It
* handles overloading of methods, and method/field name conflicts.
* All NativeJavaMethods behave as JSRef `bound' methods, in that they
* always operate on the object underlying the original NativeJavaObject
* parent regardless of any reparenting that may occur.
*
* @author Mike Shaver
* @see NativeJavaArray
* @see NativeJavaPackage
* @see NativeJavaClass
*/
public class NativeJavaMethod extends NativeFunction implements Function {
public NativeJavaMethod() {
names = new String[1];
}
public NativeJavaMethod(Method[] methods) {
this.methods = methods;
names = new String[1];
names[0] = methods[0].getName();
}
public NativeJavaMethod(Method method, String name) {
this.methods = new Method[1];
this.methods[0] = method;
names = new String[1];
names[0] = name;
}
public void add(Method method) {
if (names[0] == null) {
names[0] = method.getName();
} else if (!names[0].equals(method.getName())) {
throw new RuntimeException("internal method name mismatch");
}
// XXX a more intelligent growth algorithm would be nice
int len = methods == null ? 0 : methods.length;
Method[] newMeths = new Method[len + 1];
for (int i = 0; i < len; i++)
newMeths[i] = methods[i];
newMeths[len] = method;
methods = newMeths;
}
static String scriptSignature(Object value) {
if (value == null) {
return "null";
}
else {
Class type = value.getClass();
if (type == ScriptRuntime.UndefinedClass)
return "undefined";
if (type == ScriptRuntime.BooleanClass)
return "boolean";
if (type == ScriptRuntime.StringClass)
return "string";
if (ScriptRuntime.NumberClass.isAssignableFrom(type))
return "number";
if (value instanceof NativeJavaObject) {
return ((NativeJavaObject)value).unwrap().getClass().getName();
}
if (value instanceof Scriptable) {
if (value instanceof Function)
return "function";
return "object";
}
return javaSignature(type);
}
}
static String scriptSignature(Object[] values) {
StringBuffer sig = new StringBuffer();
for (int i = 0; i < values.length; i++) {
if (i != 0)
sig.append(',');
sig.append(scriptSignature(values[i]));
}
return sig.toString();
}
static String javaSignature(Class type) {
if (type == null) {
return "null";
}
else if (type.isArray()) {
return javaSignature(type.getComponentType()) + "[]";
}
return type.getName();
}
static String javaSignature(Class[] types) {
StringBuffer sig = new StringBuffer();
for (int i = 0; i < types.length; i++) {
if (i != 0)
sig.append(',');
sig.append(javaSignature(types[i]));
}
return sig.toString();
}
static String signature(Member member) {
Class paramTypes[];
if (member instanceof Method) {
paramTypes = ((Method) member).getParameterTypes();
return member.getName() + "(" + javaSignature(paramTypes) + ")";
}
else {
paramTypes = ((Constructor) member).getParameterTypes();
return "(" + javaSignature(paramTypes) + ")";
}
}
public Object call(Context cx, Scriptable scope, Scriptable thisObj,
Object[] args)
throws JavaScriptException
{
// Find a method that matches the types given.
if (methods.length == 0) {
throw new RuntimeException("No methods defined for call");
}
Method meth = (Method) findFunction(methods, args);
if (meth == null) {
Class c = methods[0].getDeclaringClass();
String sig = c.getName() + "." + names[0] + "(" +
scriptSignature(args) + ")";
Object errArgs[] = { sig };
throw Context.reportRuntimeError(
Context.getMessage("msg.java.no_such_method", errArgs));
}
// OPT: already retrieved in findFunction, so we should inline that
// OPT: or pass it back somehow
Class paramTypes[] = meth.getParameterTypes();
// First, we marshall the args.
for (int i = 0; i < args.length; i++) {
args[i] = NativeJavaObject.coerceType(paramTypes[i], args[i]);
}
Object javaObject;
try {
javaObject = ((NativeJavaObject) thisObj).unwrap();
}
catch (ClassCastException e) {
if (Modifier.isStatic(meth.getModifiers())) {
javaObject = null; // don't need it anyway
} else {
Object errArgs[] = { names[0] };
throw Context.reportRuntimeError(
Context.getMessage("msg.nonjava.method", errArgs));
}
}
try {
if (debug) {
printDebug("Calling", meth, args);
}
Object retval = meth.invoke(javaObject, args);
Class staticType = meth.getReturnType();
if (debug) {
Class actualType = (retval == null) ? null : retval.getClass();
System.err.println(" ----- Returned " + retval +
" actual = " + actualType +
" expect = " + staticType);
}
Object wrapped = NativeJavaObject.wrap(scope, retval, staticType);
if (debug) {
Class actualType = (wrapped == null) ? null : wrapped.getClass();
System.err.println(" ----- Wrapped as " + wrapped +
" class = " + actualType);
}
// XXX set prototype && parent
if (wrapped == Undefined.instance)
return wrapped;
if (wrapped == null && staticType == Void.TYPE)
return Undefined.instance;
if (retval != wrapped && wrapped instanceof Scriptable) {
Scriptable s = (Scriptable)wrapped;
if (s.getPrototype() == null)
s.setPrototype(parent.getPrototype());
if (s.getParentScope() == null)
s.setParentScope(parent.getParentScope());
}
return wrapped;
} catch (IllegalAccessException accessEx) {
throw Context.reportRuntimeError(accessEx.getMessage());
} catch (InvocationTargetException e) {
throw JavaScriptException.wrapException(scope, e);
}
}
/**
* Find the correct function to call given the set of methods
* or constructors and the arguments.
* If no function can be found to call, return null.
*/
static Member findFunction(Member[] methodsOrCtors, Object[] args) {
if (methodsOrCtors.length == 0)
return null;
boolean hasMethods = methodsOrCtors[0] instanceof Method;
if (Context.useJSObject &&
NativeJavaObject.jsObjectClass != null)
{
try {
for (int i = 0; i < args.length; i++) {
if (NativeJavaObject.jsObjectClass.isInstance(args[i]))
args[i] = NativeJavaObject.jsObjectGetScriptable.invoke(
args[i], ScriptRuntime.emptyArgs);
}
}
catch (InvocationTargetException e) {
// Just abandon conversion from JSObject
}
catch (IllegalAccessException e) {
// Just abandon conversion from JSObject
}
}
Member bestFit = null;
Class[] bestFitTypes = null;
java.util.Vector ambiguousMethods = null;
for (int i = 0; i < methodsOrCtors.length; i++) {
Member member = methodsOrCtors[i];
Class paramTypes[] = hasMethods
? ((Method) member).getParameterTypes()
: ((Constructor) member).getParameterTypes();
if (paramTypes.length != args.length) {
continue;
}
if (bestFitTypes == null) {
int j;
for (j = 0; j < paramTypes.length; j++) {
if (!NativeJavaObject.canConvert(args[j], paramTypes[j])) {
if (debug) printDebug("Rejecting ", member, args);
break;
}
}
if (j == paramTypes.length) {
if (debug) printDebug("Found ", member, args);
bestFit = member;
bestFitTypes = paramTypes;
}
}
else {
int preference =
NativeJavaMethod.preferSignature(args,
paramTypes,
bestFitTypes);
if (preference == PREFERENCE_AMBIGUOUS) {
if (debug) printDebug("Deferring ", member, args);
// add to "ambiguity list"
if (ambiguousMethods == null)
ambiguousMethods = new java.util.Vector();
ambiguousMethods.addElement(member);
}
else if (preference == PREFERENCE_FIRST_ARG) {
if (debug) printDebug("Substituting ", member, args);
bestFit = member;
bestFitTypes = paramTypes;
}
else {
if (debug) printDebug("Rejecting ", member, args);
}
}
}
if (ambiguousMethods == null)
return bestFit;
// Compare ambiguous methods with best fit, in case
// the current best fit removes the ambiguities.
for (int i = ambiguousMethods.size() - 1; i >= 0 ; i--) {
Member member = (Member)ambiguousMethods.elementAt(i);
Class paramTypes[] = hasMethods
? ((Method) member).getParameterTypes()
: ((Constructor) member).getParameterTypes();
int preference =
NativeJavaMethod.preferSignature(args,
paramTypes,
bestFitTypes);
if (preference == PREFERENCE_FIRST_ARG) {
if (debug) printDebug("Substituting ", member, args);
bestFit = member;
bestFitTypes = paramTypes;
ambiguousMethods.removeElementAt(i);
}
else if (preference == PREFERENCE_SECOND_ARG) {
if (debug) printDebug("Rejecting ", member, args);
ambiguousMethods.removeElementAt(i);
}
else {
if (debug) printDebug("UNRESOLVED: ", member, args);
}
}
if (ambiguousMethods.size() > 0) {
// PENDING: report remaining ambiguity
StringBuffer buf = new StringBuffer();
boolean isCtor = (bestFit instanceof Constructor);
ambiguousMethods.addElement(bestFit);
for (int i = 0; i < ambiguousMethods.size(); i++) {
if (i != 0) {
buf.append(", ");
}
Member member = (Member)ambiguousMethods.elementAt(i);
if (!isCtor) {
Class rtnType = ((Method)member).getReturnType();
buf.append(rtnType);
buf.append(' ');
}
buf.append(NativeJavaMethod.signature(member));
}
String errMsg;
if (isCtor) {
Object errArgs[] = {
bestFit.getName(),
NativeJavaMethod.scriptSignature(args),
buf.toString()
};
errMsg =
Context.getMessage("msg.constructor.ambiguous", errArgs);
}
else {
Object errArgs[] = {
bestFit.getDeclaringClass().getName(),
bestFit.getName(),
NativeJavaMethod.scriptSignature(args),
buf.toString()
};
errMsg = Context.getMessage("msg.method.ambiguous", errArgs);
}
throw
Context.reportRuntimeError(errMsg);
}
return bestFit;
}
/** Types are equal */
static final int PREFERENCE_EQUAL = 0;
static final int PREFERENCE_FIRST_ARG = 1;
static final int PREFERENCE_SECOND_ARG = 2;
/** No clear "easy" conversion */
static final int PREFERENCE_AMBIGUOUS = 3;
/**
* Determine which of two signatures is the closer fit.
* Returns one of PREFERENCE_EQUAL, PREFERENCE_FIRST_ARG,
* PREFERENCE_SECOND_ARG, or PREFERENCE_AMBIGUOUS.
*/
public static int preferSignature(Object[] args,
Class[] sig1, Class[] sig2) {
int preference = 0;
for (int j = 0; j < args.length; j++) {
Class type1 = sig1[j];
Class type2 = sig2[j];
if (type1 == type2) {
continue;
}
preference |=
NativeJavaMethod.preferConversion(args[j],
type1,
type2);
if (preference == PREFERENCE_AMBIGUOUS) {
break;
}
}
return preference;
}
/**
* Determine which of two types is the easier conversion.
* Returns one of PREFERENCE_EQUAL, PREFERENCE_FIRST_ARG,
* PREFERENCE_SECOND_ARG, or PREFERENCE_AMBIGUOUS.
*/
public static int preferConversion(Object fromObj,
Class toClass1, Class toClass2) {
int rank1 =
NativeJavaObject.getConversionWeight(fromObj, toClass1);
int rank2 =
NativeJavaObject.getConversionWeight(fromObj, toClass2);
if (rank1 == NativeJavaObject.CONVERSION_NONTRIVIAL &&
rank2 == NativeJavaObject.CONVERSION_NONTRIVIAL) {
if (toClass1.isAssignableFrom(toClass2)) {
return PREFERENCE_SECOND_ARG;
}
else if (toClass2.isAssignableFrom(toClass1)) {
return PREFERENCE_FIRST_ARG;
}
}
else {
if (rank1 < rank2) {
return PREFERENCE_FIRST_ARG;
}
else if (rank1 > rank2) {
return PREFERENCE_SECOND_ARG;
}
}
return PREFERENCE_AMBIGUOUS;
}
Method[] getMethods() {
return methods;
}
private static final boolean debug = false;
private static void printDebug(String msg, Member member, Object[] args) {
if (debug) {
System.err.println(" ----- " + msg +
member.getDeclaringClass().getName() +
"." + signature(member) +
" for arguments (" + scriptSignature(args) + ")");
}
}
Method methods[];
}

View File

@@ -1,850 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
package org.mozilla.javascript;
import java.lang.reflect.*;
import java.util.Hashtable;
import java.util.Enumeration;
/**
* This class reflects non-Array Java objects into the JavaScript environment. It
* reflect fields directly, and uses NativeJavaMethod objects to reflect (possibly
* overloaded) methods.<p>
*
* @author Mike Shaver
* @see NativeJavaArray
* @see NativeJavaPackage
* @see NativeJavaClass
*/
public class NativeJavaObject implements Scriptable, Wrapper {
public NativeJavaObject(Scriptable scope, Object javaObject,
JavaMembers members)
{
this.parent = scope;
this.javaObject = javaObject;
this.members = members;
}
public NativeJavaObject(Scriptable scope, Object javaObject,
Class staticType)
{
this.parent = scope;
this.javaObject = javaObject;
Class dynamicType = javaObject != null ? javaObject.getClass()
: staticType;
members = JavaMembers.lookupClass(scope, dynamicType, staticType);
fieldAndMethods = members.getFieldAndMethodsObjects(javaObject, false);
}
public boolean has(String name, Scriptable start) {
return members.has(name, false);
}
public boolean has(int index, Scriptable start) {
return false;
}
public Object get(String name, Scriptable start) {
if (fieldAndMethods != null) {
Object result = fieldAndMethods.get(name);
if (result != null)
return result;
}
// TODO: passing 'this' as the scope is bogus since it has
// no parent scope
return members.get(this, name, javaObject, false);
}
public Object get(int index, Scriptable start) {
throw members.reportMemberNotFound(Integer.toString(index));
}
public void put(String name, Scriptable start, Object value) {
members.put(name, javaObject, value, false);
}
public void put(int index, Scriptable start, Object value) {
throw members.reportMemberNotFound(Integer.toString(index));
}
public boolean hasInstance(Scriptable value) {
// This is an instance of a Java class, so always return false
return false;
}
public void delete(String name) {
}
public void delete(int index) {
}
public Scriptable getPrototype() {
if (javaObject.getClass() == ScriptRuntime.StringClass) {
return ScriptableObject.getClassPrototype(parent, "String");
}
return null;
}
public void setPrototype(Scriptable prototype) {
}
/**
* Returns the parent (enclosing) scope of the object.
*/
public Scriptable getParentScope() {
return parent;
}
/**
* Sets the parent (enclosing) scope of the object.
*/
public void setParentScope(Scriptable m) {
parent = m;
}
public Object[] getIds() {
return members.getIds(false);
}
public static Object wrap(Scriptable scope, Object obj, Class staticType)
{
if (obj == null)
return obj;
Class cls = obj.getClass();
if (staticType != null && staticType.isPrimitive()) {
if (staticType == Void.TYPE)
return Undefined.instance;
if (staticType == Character.TYPE)
return new Integer((int) ((Character) obj).charValue());
return obj;
}
if (cls.isArray())
return NativeJavaArray.wrap(scope, obj);
if (obj instanceof Scriptable)
return obj;
if (Context.useJSObject && jsObjectClass != null &&
staticType != jsObjectClass && jsObjectClass.isInstance(obj))
{
try {
return jsObjectGetScriptable.invoke(obj, ScriptRuntime.emptyArgs);
}
catch (InvocationTargetException e) {
// Just abandon conversion from JSObject
}
catch (IllegalAccessException e) {
// Just abandon conversion from JSObject
}
}
return new NativeJavaObject(scope, obj, staticType);
}
public Object unwrap() {
return javaObject;
}
public String getClassName() {
return "JavaObject";
}
Function getConverter(String converterName) {
Object converterFunction = get(converterName, this);
if (converterFunction instanceof Function) {
return (Function) converterFunction;
}
return null;
}
Object callConverter(Function converterFunction)
throws JavaScriptException
{
Function f = (Function) converterFunction;
return f.call(Context.getContext(), f.getParentScope(),
this, new Object[0]);
}
Object callConverter(String converterName)
throws JavaScriptException
{
Function converter = getConverter(converterName);
if (converter == null) {
Object[] errArgs = { converterName, javaObject.getClass().getName() };
throw Context.reportRuntimeError(
Context.getMessage("msg.java.conversion.implicit_method",
errArgs));
}
return callConverter(converter);
}
public Object getDefaultValue(Class hint) {
if (hint == null || hint == ScriptRuntime.StringClass)
return javaObject.toString();
try {
if (hint == ScriptRuntime.BooleanClass)
return callConverter("booleanValue");
if (hint == ScriptRuntime.NumberClass) {
return callConverter("doubleValue");
}
// fall through to error message
} catch (JavaScriptException jse) {
// fall through to error message
}
throw Context.reportRuntimeError(
Context.getMessage("msg.default.value", null));
}
/**
* Determine whether we can/should convert between the given type and the
* desired one. This should be superceded by a conversion-cost calculation
* function, but for now I'll hide behind precedent.
*/
public static boolean canConvert(Object fromObj, Class to) {
int weight = NativeJavaObject.getConversionWeight(fromObj, to);
return (weight < CONVERSION_NONE);
}
static final int JSTYPE_UNDEFINED = 0; // undefined type
static final int JSTYPE_NULL = 1; // null
static final int JSTYPE_BOOLEAN = 2; // boolean
static final int JSTYPE_NUMBER = 3; // number
static final int JSTYPE_STRING = 4; // string
static final int JSTYPE_JAVA_CLASS = 5; // JavaClass
static final int JSTYPE_JAVA_OBJECT = 6; // JavaObject
static final int JSTYPE_JAVA_ARRAY = 7; // JavaArray
static final int JSTYPE_OBJECT = 8; // Scriptable
public static final byte CONVERSION_TRIVIAL = 1;
public static final byte CONVERSION_NONTRIVIAL = 0;
public static final byte CONVERSION_NONE = 99;
/**
* Derive a ranking based on how "natural" the conversion is.
* The special value CONVERSION_NONE means no conversion is possible,
* and CONVERSION_NONTRIVIAL signals that more type conformance testing
* is required.
* Based on
* <a href="http://www.mozilla.org/js/liveconnect/lc3_method_overloading.html">
* "preferred method conversions" from Live Connect 3</a>
*/
public static int getConversionWeight(Object fromObj, Class to) {
int fromCode = NativeJavaObject.getJSTypeCode(fromObj);
int result = CONVERSION_NONE;
switch (fromCode) {
case JSTYPE_UNDEFINED:
if (to == ScriptRuntime.StringClass ||
to == ScriptRuntime.ObjectClass) {
result = 1;
}
break;
case JSTYPE_NULL:
if (!to.isPrimitive()) {
result = 1;
}
break;
case JSTYPE_BOOLEAN:
// "boolean" is #1
if (to == Boolean.TYPE) {
result = 1;
}
else if (to == ScriptRuntime.BooleanClass) {
result = 2;
}
else if (to == ScriptRuntime.ObjectClass) {
result = 3;
}
else if (to == ScriptRuntime.StringClass) {
result = 4;
}
break;
case JSTYPE_NUMBER:
if (to.isPrimitive()) {
if (to == Double.TYPE) {
result = 1;
}
else if (to != Boolean.TYPE) {
result = 1 + NativeJavaObject.getSizeRank(to);
}
}
else {
if (to == ScriptRuntime.StringClass) {
// native numbers are #1-8
result = 9;
}
else if (to == ScriptRuntime.ObjectClass) {
result = 10;
}
else if (ScriptRuntime.NumberClass.isAssignableFrom(to)) {
// "double" is #1
result = 2;
}
}
break;
case JSTYPE_STRING:
if (to == ScriptRuntime.StringClass) {
result = 1;
}
else if (to == ScriptRuntime.ObjectClass) {
result = 2;
}
else if (to.isPrimitive() && to != Boolean.TYPE) {
if (to == Character.TYPE) {
result = 3;
}
else {
result = 4;
}
}
break;
case JSTYPE_JAVA_CLASS:
if (to == ScriptRuntime.ClassClass) {
result = 1;
}
else if (Context.useJSObject && jsObjectClass != null &&
jsObjectClass.isAssignableFrom(to)) {
result = 2;
}
else if (to == ScriptRuntime.ObjectClass) {
result = 3;
}
else if (to == ScriptRuntime.StringClass) {
result = 4;
}
break;
case JSTYPE_JAVA_OBJECT:
case JSTYPE_JAVA_ARRAY:
if (to == ScriptRuntime.StringClass) {
result = 2;
}
else if (to.isPrimitive() && to != Boolean.TYPE) {
result =
(fromCode == JSTYPE_JAVA_ARRAY) ?
CONVERSION_NONTRIVIAL :
2 + NativeJavaObject.getSizeRank(to);
}
else {
Object javaObj = fromObj;
if (javaObj instanceof NativeJavaObject) {
javaObj = ((NativeJavaObject)javaObj).unwrap();
}
if (to.isInstance(javaObj)) {
result = CONVERSION_NONTRIVIAL;
}
}
break;
case JSTYPE_OBJECT:
// Other objects takes #1-#3 spots
if (Context.useJSObject && jsObjectClass != null &&
jsObjectClass.isAssignableFrom(to)) {
result = 1;
}
else if (to == ScriptRuntime.ObjectClass) {
result = 2;
}
else if (to == ScriptRuntime.StringClass) {
result = 3;
}
else if (to.isPrimitive() || to != Boolean.TYPE) {
result = 3 + NativeJavaObject.getSizeRank(to);
}
break;
}
return result;
}
static int getSizeRank(Class aType) {
if (aType == Double.TYPE) {
return 1;
}
else if (aType == Float.TYPE) {
return 2;
}
else if (aType == Long.TYPE) {
return 3;
}
else if (aType == Integer.TYPE) {
return 4;
}
else if (aType == Short.TYPE) {
return 5;
}
else if (aType == Character.TYPE) {
return 6;
}
else if (aType == Byte.TYPE) {
return 7;
}
else if (aType == Boolean.TYPE) {
return CONVERSION_NONE;
}
else {
return 8;
}
}
static int getJSTypeCode(Object value) {
if (value == null) {
return JSTYPE_NULL;
}
else if (value == Undefined.instance) {
return JSTYPE_UNDEFINED;
}
else if (value instanceof Scriptable) {
if (value instanceof NativeJavaClass) {
return JSTYPE_JAVA_CLASS;
}
else if (value instanceof NativeJavaArray) {
return JSTYPE_JAVA_ARRAY;
}
else if (value instanceof NativeJavaObject) {
return JSTYPE_JAVA_OBJECT;
}
else {
return JSTYPE_OBJECT;
}
}
else {
Class valueClass = value.getClass();
if (valueClass == ScriptRuntime.StringClass) {
return JSTYPE_STRING;
}
else if (valueClass == ScriptRuntime.BooleanClass) {
return JSTYPE_BOOLEAN;
}
else if (value instanceof Number) {
return JSTYPE_NUMBER;
}
else if (valueClass == ScriptRuntime.ClassClass) {
return JSTYPE_JAVA_CLASS;
}
else if (valueClass.isArray()) {
return JSTYPE_JAVA_ARRAY;
}
else {
return JSTYPE_JAVA_OBJECT;
}
}
}
/**
* Type-munging for field setting and method invocation.
* Conforms to LC3 specification
*/
public static Object coerceType(Class type, Object value) {
if (value != null && value.getClass() == type) {
return value;
}
switch (NativeJavaObject.getJSTypeCode(value)) {
case JSTYPE_NULL:
// raise error if type.isPrimitive()
if (type.isPrimitive()) {
reportConversionError(value, type);
}
return null;
case JSTYPE_UNDEFINED:
if (type == ScriptRuntime.StringClass ||
type == ScriptRuntime.ObjectClass) {
return "undefined";
}
else {
reportConversionError("undefined", type);
}
break;
case JSTYPE_BOOLEAN:
// Under LC3, only JS Booleans can be coerced into a Boolean value
if (type == Boolean.TYPE ||
type == ScriptRuntime.BooleanClass ||
type == ScriptRuntime.ObjectClass) {
return value;
}
else if (type == ScriptRuntime.StringClass) {
return value.toString();
}
else {
reportConversionError(value, type);
}
break;
case JSTYPE_NUMBER:
if (type == ScriptRuntime.StringClass) {
return ScriptRuntime.toString(value);
}
else if (type == ScriptRuntime.ObjectClass) {
return coerceToNumber(Double.TYPE, value);
}
else if ((type.isPrimitive() && type != Boolean.TYPE) ||
ScriptRuntime.NumberClass.isAssignableFrom(type)) {
return coerceToNumber(type, value);
}
else {
reportConversionError(value, type);
}
break;
case JSTYPE_STRING:
if (type == ScriptRuntime.StringClass ||
type == ScriptRuntime.ObjectClass) {
return value;
}
else if (type == Character.TYPE ||
type == ScriptRuntime.CharacterClass) {
// Special case for converting a single char string to a
// character
// Placed here because it applies *only* to JS strings,
// not other JS objects converted to strings
if (((String)value).length() == 1) {
return new Character(((String)value).charAt(0));
}
else {
return coerceToNumber(type, value);
}
}
else if ((type.isPrimitive() && type != Boolean.TYPE) ||
ScriptRuntime.NumberClass.isAssignableFrom(type)) {
return coerceToNumber(type, value);
}
else {
reportConversionError(value, type);
}
break;
case JSTYPE_JAVA_CLASS:
if (Context.useJSObject && jsObjectClass != null &&
(type == ScriptRuntime.ObjectClass ||
jsObjectClass.isAssignableFrom(type))) {
return coerceToJSObject(type, (Scriptable)value);
}
else {
if (value instanceof Wrapper) {
value = ((Wrapper)value).unwrap();
}
if (type == ScriptRuntime.ClassClass ||
type == ScriptRuntime.ObjectClass) {
return value;
}
else if (type == ScriptRuntime.StringClass) {
return value.toString();
}
else {
reportConversionError(value, type);
}
}
break;
case JSTYPE_JAVA_OBJECT:
case JSTYPE_JAVA_ARRAY:
if (type.isPrimitive()) {
if (type == Boolean.TYPE) {
reportConversionError(value, type);
}
return coerceToNumber(type, value);
}
else {
if (value instanceof Wrapper) {
value = ((Wrapper)value).unwrap();
}
if (type == ScriptRuntime.StringClass) {
return value.toString();
}
else {
if (type.isInstance(value)) {
return value;
}
else {
reportConversionError(value, type);
}
}
}
break;
case JSTYPE_OBJECT:
if (Context.useJSObject && jsObjectClass != null &&
(type == ScriptRuntime.ObjectClass ||
jsObjectClass.isAssignableFrom(type))) {
return coerceToJSObject(type, (Scriptable)value);
}
else if (type == ScriptRuntime.StringClass) {
return ScriptRuntime.toString(value);
}
else if (type.isPrimitive()) {
if (type == Boolean.TYPE) {
reportConversionError(value, type);
}
return coerceToNumber(type, value);
}
else if (type.isInstance(value)) {
return value;
}
else {
reportConversionError(value, type);
}
break;
}
return value;
}
static Object coerceToJSObject(Class type, Scriptable value) {
// If JSObject compatibility is enabled, and the method wants it,
// wrap the Scriptable value in a JSObject.
if (ScriptRuntime.ScriptableClass.isAssignableFrom(type))
return value;
try {
Object ctorArgs[] = { value };
return jsObjectCtor.newInstance(ctorArgs);
} catch (InstantiationException instEx) {
throw new EvaluatorException("error generating JSObject wrapper for " +
value);
} catch (IllegalArgumentException argEx) {
throw new EvaluatorException("JSObject constructor doesn't want [Scriptable]!");
} catch (InvocationTargetException e) {
throw WrappedException.wrapException(e);
} catch (IllegalAccessException accessEx) {
throw new EvaluatorException("JSObject constructor is protected/private!");
}
}
static Object coerceToNumber(Class type, Object value) {
Class valueClass = value.getClass();
// Character
if (type == Character.TYPE || type == ScriptRuntime.CharacterClass) {
if (valueClass == ScriptRuntime.CharacterClass) {
return value;
}
return new Character((char)toInteger(value,
ScriptRuntime.CharacterClass,
Character.MIN_VALUE,
Character.MAX_VALUE));
}
// Double, Float
if (type == ScriptRuntime.ObjectClass ||
type == ScriptRuntime.DoubleClass || type == Double.TYPE) {
return valueClass == ScriptRuntime.DoubleClass
? value
: new Double(toDouble(value));
}
if (type == ScriptRuntime.FloatClass || type == Float.TYPE) {
if (valueClass == ScriptRuntime.FloatClass) {
return value;
}
else {
double number = toDouble(value);
if (Double.isInfinite(number) || Double.isNaN(number)
|| number == 0.0) {
return new Float((float)number);
}
else {
double absNumber = Math.abs(number);
if (absNumber < (double)Float.MIN_VALUE) {
return new Float((number > 0.0) ? +0.0 : -0.0);
}
else if (absNumber > (double)Float.MAX_VALUE) {
return new Float((number > 0.0) ?
Float.POSITIVE_INFINITY :
Float.NEGATIVE_INFINITY);
}
else {
return new Float((float)number);
}
}
}
}
// Integer, Long, Short, Byte
if (type == ScriptRuntime.IntegerClass || type == Integer.TYPE) {
if (valueClass == ScriptRuntime.IntegerClass) {
return value;
}
else {
return new Integer((int)toInteger(value,
ScriptRuntime.IntegerClass,
Integer.MIN_VALUE,
Integer.MAX_VALUE));
}
}
if (type == ScriptRuntime.LongClass || type == Long.TYPE) {
if (valueClass == ScriptRuntime.LongClass) {
return value;
}
else {
return new Long(toInteger(value,
ScriptRuntime.LongClass,
Long.MIN_VALUE,
Long.MAX_VALUE));
}
}
if (type == ScriptRuntime.ShortClass || type == Short.TYPE) {
if (valueClass == ScriptRuntime.ShortClass) {
return value;
}
else {
return new Short((short)toInteger(value,
ScriptRuntime.ShortClass,
Short.MIN_VALUE,
Short.MAX_VALUE));
}
}
if (type == ScriptRuntime.ByteClass || type == Byte.TYPE) {
if (valueClass == ScriptRuntime.ByteClass) {
return value;
}
else {
return new Byte((byte)toInteger(value,
ScriptRuntime.ByteClass,
Byte.MIN_VALUE,
Byte.MAX_VALUE));
}
}
return new Double(toDouble(value));
}
static double toDouble(Object value) {
if (value instanceof Number) {
return ((Number)value).doubleValue();
}
else if (value instanceof String) {
return ScriptRuntime.toNumber((String)value);
}
else if (value instanceof Scriptable) {
if (value instanceof Wrapper) {
// XXX: optimize tail-recursion?
return toDouble(((Wrapper)value).unwrap());
}
else {
return ScriptRuntime.toNumber(value);
}
}
else {
double result = Double.NaN;
Method meth;
try {
meth = value.getClass().getMethod("doubleValue", null);
}
catch (NoSuchMethodException e) {
meth = null;
}
catch (SecurityException e) {
meth = null;
}
if (meth != null) {
try {
return ((Number)meth.invoke(value, null)).doubleValue();
}
catch (IllegalAccessException e) {
// XXX: ignore, or error message?
reportConversionError(value, Double.TYPE);
}
catch (InvocationTargetException e) {
// XXX: ignore, or error message?
reportConversionError(value, Double.TYPE);
}
}
return ScriptRuntime.toNumber(value.toString());
}
}
static long toInteger(Object value, Class type, long min, long max) {
double d = toDouble(value);
if (Double.isInfinite(d) || Double.isNaN(d)) {
// Convert to string first, for more readable message
reportConversionError(ScriptRuntime.toString(value), type);
}
if (d > 0.0) {
d = Math.floor(d);
}
else {
d = Math.ceil(d);
}
if (d < (double)min || d > (double)max) {
// Convert to string first, for more readable message
reportConversionError(ScriptRuntime.toString(value), type);
}
return (long)d;
}
static void reportConversionError(Object value, Class type) {
Object[] args = {value, type};
throw Context.reportRuntimeError(Context.getMessage("msg.conversion.not.allowed", args));
}
public static void initJSObject() {
if (!Context.useJSObject)
return;
// if netscape.javascript.JSObject is in the CLASSPATH, enable JSObject
// compatability wrappers
jsObjectClass = null;
try {
jsObjectClass = Class.forName("netscape.javascript.JSObject");
Class ctorParms[] = { ScriptRuntime.ScriptableClass };
jsObjectCtor = jsObjectClass.getConstructor(ctorParms);
jsObjectGetScriptable = jsObjectClass.getMethod("getScriptable",
new Class[0]);
} catch (ClassNotFoundException classEx) {
// jsObjectClass already null
} catch (NoSuchMethodException methEx) {
// jsObjectClass already null
}
}
/**
* The parent scope of this object.
*/
protected Scriptable parent;
protected Object javaObject;
protected JavaMembers members;
private Hashtable fieldAndMethods;
static Class jsObjectClass;
static Constructor jsObjectCtor;
static Method jsObjectGetScriptable;
}

View File

@@ -1,214 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
package org.mozilla.javascript;
import java.lang.reflect.*;
/**
* This class reflects Java packages into the JavaScript environment. We
* lazily reflect classes and subpackages, and use a caching/sharing
* system to ensure that members reflected into one JavaPackage appear
* in all other references to the same package (as with Packages.java.lang
* and java.lang).
*
* @author Mike Shaver
* @see NativeJavaArray
* @see NativeJavaObject
* @see NativeJavaClass
*/
public class NativeJavaPackage extends ScriptableObject {
// we know these are packages so we can skip the class check
// note that this is ok even if the package isn't present.
static final String[] commonPackages = {
"java.lang",
"java.lang.reflect",
"java.io",
"java.math",
"java.util",
"java.util.zip",
"java.text",
"java.text.resources",
"java.applet",
};
public static Scriptable init(Scriptable scope)
throws PropertyException
{
NativeJavaPackage packages = new NativeJavaPackage("");
packages.setPrototype(getObjectPrototype(scope));
packages.setParentScope(scope);
// We want to get a real alias, and not a distinct JavaPackage
// with the same packageName, so that we share classes and packages
// that are underneath.
NativeJavaPackage javaAlias = (NativeJavaPackage)packages.get("java",
packages);
// It's safe to downcast here since initStandardObjects takes
// a ScriptableObject.
ScriptableObject global = (ScriptableObject) scope;
global.defineProperty("Packages", packages, ScriptableObject.DONTENUM);
global.defineProperty("java", javaAlias, ScriptableObject.DONTENUM);
for (int i = 0; i < commonPackages.length; i++)
packages.forcePackage(commonPackages[i]);
NativeJavaObject.initJSObject();
String[] methods = { "getClass" };
global.defineFunctionProperties(methods, NativeJavaPackage.class,
ScriptableObject.DONTENUM);
// I think I'm supposed to return the prototype, but I don't have one.
return packages;
}
// set up a name which is known to be a package so we don't
// need to look for a class by that name
void forcePackage(String name) {
NativeJavaPackage pkg;
int end = name.indexOf('.');
if (end == -1)
end = name.length();
String id = name.substring(0, end);
Object cached = super.get(id, this);
if (cached != null && cached instanceof NativeJavaPackage) {
pkg = (NativeJavaPackage) cached;
} else {
String newPackage = packageName.length() == 0
? id
: packageName + "." + id;
pkg = new NativeJavaPackage(newPackage);
pkg.setParentScope(this);
pkg.setPrototype(this.prototype);
super.put(id, this, pkg);
}
if (end < name.length())
pkg.forcePackage(name.substring(end+1));
}
public NativeJavaPackage(String packageName) {
this.packageName = packageName;
}
public String getClassName() {
return "JavaPackage";
}
public boolean has(String id, int index, Scriptable start) {
return true;
}
public void put(String id, Scriptable start, Object value) {
// Can't add properties to Java packages. Sorry.
}
public void put(int index, Scriptable start, Object value) {
// Can't add properties to Java packages. Sorry.
}
public Object get(String id, Scriptable start) {
return getPkgProperty(id, start, true);
}
synchronized Object getPkgProperty(String name, Scriptable start,
boolean createPkg)
{
Object cached = super.get(name, start);
if (cached != NOT_FOUND)
return cached;
String newPackage = packageName.length() == 0
? name
: packageName + "." + name;
Context cx = Context.getContext();
SecuritySupport ss = cx.getSecuritySupport();
Scriptable newValue;
try {
if (ss != null && !ss.visibleToScripts(newPackage))
throw new ClassNotFoundException();
Class newClass = Class.forName(newPackage);
newValue = NativeJavaClass.wrap(getTopLevelScope(this), newClass);
newValue.setParentScope(this);
newValue.setPrototype(this.prototype);
} catch (ClassNotFoundException ex) {
if (createPkg) {
NativeJavaPackage pkg = new NativeJavaPackage(newPackage);
pkg.setParentScope(this);
pkg.setPrototype(this.prototype);
newValue = pkg;
} else {
newValue = null;
}
}
if (newValue != null) {
// Make it available for fast lookup and sharing of
// lazily-reflected constructors and static members.
super.put(name, start, newValue);
}
return newValue;
}
public Object get(int index, Scriptable start) {
return NOT_FOUND;
}
public Object getDefaultValue(Class ignored) {
return toString();
}
public String toString() {
return "[JavaPackage " + packageName + "]";
}
public static Scriptable getClass(Context cx, Scriptable thisObj,
Object[] args, Function funObj)
{
if (args.length > 0 && args[0] instanceof NativeJavaObject) {
NativeJavaObject nativeJavaObj = (NativeJavaObject) args[0];
Scriptable result = getTopLevelScope(thisObj);
Class cl = nativeJavaObj.unwrap().getClass();
// Evaluate the class name by getting successive properties of
// the string to find the appropriate NativeJavaClass object
String name = "Packages." + cl.getName();
int offset = 0;
for (;;) {
int index = name.indexOf('.', offset);
String propName = index == -1
? name.substring(offset)
: name.substring(offset, index);
Object prop = result.get(propName, result);
if (!(prop instanceof Scriptable))
break; // fall through to error
result = (Scriptable) prop;
if (index == -1)
return result;
offset = index+1;
}
}
throw Context.reportRuntimeError(
Context.getMessage("msg.not.java.obj", null));
}
private String packageName;
}

View File

@@ -1,122 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
package org.mozilla.javascript;
/**
* This class implements the Math native object.
* See ECMA 15.8.
* @author Norris Boyd
*/
public class NativeMath extends ScriptableObject {
public static Scriptable init(Scriptable scope)
throws PropertyException
{
NativeMath m = new NativeMath();
m.setPrototype(getObjectPrototype(scope));
m.setParentScope(scope);
String[] names = { "acos", "asin", "atan", "atan2", "ceil",
"cos", "floor", "log", "random",
"sin", "sqrt", "tan" };
m.defineFunctionProperties(names, java.lang.Math.class,
ScriptableObject.DONTENUM);
// These functions exist in java.lang.Math, but
// are overloaded. Define our own wrappers.
String[] localNames = { "abs", "exp", "max", "min", "round", "pow" };
m.defineFunctionProperties(localNames, NativeMath.class,
ScriptableObject.DONTENUM);
final int attr = ScriptableObject.DONTENUM |
ScriptableObject.PERMANENT |
ScriptableObject.READONLY;
m.defineProperty("E", new Double(Math.E), attr);
m.defineProperty("PI", new Double(Math.PI), attr);
m.defineProperty("LN10", new Double(2.302585092994046), attr);
m.defineProperty("LN2", new Double(0.6931471805599453), attr);
m.defineProperty("LOG2E", new Double(1.4426950408889634), attr);
m.defineProperty("LOG10E", new Double(0.4342944819032518), attr);
m.defineProperty("SQRT1_2", new Double(0.7071067811865476), attr);
m.defineProperty("SQRT2", new Double(1.4142135623730951), attr);
// We know that scope is a Scriptable object since we
// constrained the type on initStandardObjects.
ScriptableObject global = (ScriptableObject) scope;
global.defineProperty("Math", m, ScriptableObject.DONTENUM);
return m;
}
public NativeMath() {
}
public String getClassName() {
return "Math";
}
public double abs(double d) {
if (d == 0.0)
return 0.0; // abs(-0.0) should be 0.0, but -0.0 < 0.0 == false
else if (d < 0.0)
return -d;
else
return d;
}
public double max(double x, double y) {
return Math.max(x, y);
}
public double min(double x, double y) {
return Math.min(x, y);
}
public double round(double d) {
if (d != d)
return d; // NaN
if (d == Double.POSITIVE_INFINITY || d == Double.NEGATIVE_INFINITY)
return d;
long l = Math.round(d);
if (l == 0) {
// We must propagate the sign of d into the result
if (d < 0.0)
return ScriptRuntime.negativeZero;
return d == 0.0 ? d : 0.0;
}
return (double) l;
}
public double pow(double x, double y) {
if (y == 0)
return 1.0; // Java's pow(NaN, 0) = NaN; we need 1
return Math.pow(x, y);
}
public double exp(double d) {
if (d == Double.POSITIVE_INFINITY)
return d;
if (d == Double.NEGATIVE_INFINITY)
return 0.0;
return Math.exp(d);
}
}

Some files were not shown because too many files have changed in this diff Show More