Mozilla/mozilla/js2/src/js2eval.cpp
rogerl%netscape.com 9c6cd66f8f Minor e3 fixes. Adding type handling for function parameters & result.
git-svn-id: svn://10.0.0.236/trunk@142306 18797224-902f-48f8-a5cc-f745e15eee43
2003-05-10 19:30:34 +00:00

983 lines
37 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the JavaScript 2 Prototype.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU Public License (the "GPL"), in which case the
* provisions of the GPL are applicable instead of those above.
* If you wish to allow use of your version of this file only
* under the terms of the GPL and not to allow others to use your
* version of this file under the NPL, indicate your decision by
* deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete
* the provisions above, a recipient may use your version of this
* file under either the NPL or the GPL.
*/
#ifdef _WIN32
#include "msvc_pragma.h"
#endif
#include <algorithm>
#include <assert.h>
#include <map>
#include <list>
#include <stack>
#include "world.h"
#include "utilities.h"
#include "js2value.h"
#include "jslong.h"
#include "numerics.h"
#include "reader.h"
#include "parser.h"
#include "regexp.h"
#include "js2engine.h"
#include "bytecodecontainer.h"
#include "js2metadata.h"
namespace JavaScript {
namespace MetaData {
js2val JS2Metadata::readEvalString(const String &str, const String& fileName)
{
js2val result = JS2VAL_VOID;
Arena a;
Pragma::Flags flags = Pragma::js1;
Parser p(world, a, flags, str, fileName);
CompilationData *oldData = NULL;
try {
StmtNode *parsedStatements = p.parseProgram();
ASSERT(p.lexer.peek(true).hasKind(Token::end));
if (showTrees)
{
PrettyPrinter f(stdOut, 80);
{
PrettyPrinter::Block b(f, 2);
f << "Program =";
f.linearBreak(1);
StmtNode::printStatements(f, parsedStatements);
}
f.end();
stdOut << '\n';
}
if (parsedStatements) {
oldData = startCompilationUnit(NULL, str, fileName);
ValidateStmtList(parsedStatements);
result = ExecuteStmtList(RunPhase, parsedStatements);
}
}
catch (Exception &x) {
if (oldData)
restoreCompilationUnit(oldData);
throw x;
}
if (oldData)
restoreCompilationUnit(oldData);
return result;
}
js2val JS2Metadata::readEvalFile(const String& fileName)
{
String buffer;
int ch;
js2val result = JS2VAL_VOID;
std::string str(fileName.length(), char());
std::transform(fileName.begin(), fileName.end(), str.begin(), narrow);
FILE* f = fopen(str.c_str(), "r");
if (f) {
while ((ch = getc(f)) != EOF)
buffer += static_cast<char>(ch);
fclose(f);
result = readEvalString(buffer, fileName);
}
return result;
}
js2val JS2Metadata::readEvalFile(const char *fileName)
{
String buffer;
int ch;
js2val result = JS2VAL_VOID;
FILE* f = fopen(fileName, "r");
if (f) {
while ((ch = getc(f)) != EOF)
buffer += static_cast<char>(ch);
fclose(f);
result = readEvalString(buffer, widenCString(fileName));
}
return result;
}
/*
* Evaluate the linked list of statement nodes beginning at 'p'
* (generate bytecode and then execute that bytecode
*/
js2val JS2Metadata::ExecuteStmtList(Phase phase, StmtNode *p)
{
Arena *oldArena = referenceArena;
referenceArena = new Arena;
size_t lastPos;
try {
lastPos = p->pos;
while (p) {
SetupStmt(env, phase, p);
lastPos = p->pos;
p = p->next;
}
}
catch (Exception &x) {
referenceArena->clear();
delete referenceArena;
referenceArena = oldArena;
throw x;
}
referenceArena->clear();
delete referenceArena;
referenceArena = oldArena;
bCon->emitOp(eReturnVoid, lastPos);
uint8 *savePC = engine->pc;
engine->pc = NULL;
js2val retval = engine->interpret(phase, bCon, env);
engine->pc = savePC;
return retval;
}
/*
* Evaluate an expression 'p' AND execute the associated bytecode
*/
js2val JS2Metadata::EvalExpression(Environment *env, Phase phase, ExprNode *p)
{
js2val retval;
uint8 *savePC = NULL;
JS2Class *exprType;
Arena *oldArena = referenceArena;
referenceArena = new Arena;
CompilationData *oldData = startCompilationUnit(NULL, bCon->mSource, bCon->mSourceLocation);
try {
Reference *r = SetupExprNode(env, phase, p, &exprType);
if (r) r->emitReadBytecode(bCon, p->pos);
bCon->emitOp(eReturn, p->pos);
savePC = engine->pc;
engine->pc = NULL;
retval = engine->interpret(phase, bCon, env);
}
catch (Exception &x) {
referenceArena->clear();
delete referenceArena;
referenceArena = oldArena;
engine->pc = savePC;
restoreCompilationUnit(oldData);
throw x;
}
referenceArena->clear();
delete referenceArena;
referenceArena = oldArena;
engine->pc = savePC;
restoreCompilationUnit(oldData);
return retval;
}
// Execute an expression and return the result, which must be a type
JS2Class *JS2Metadata::EvalTypeExpression(Environment *env, Phase phase, ExprNode *p)
{
js2val retval = EvalExpression(env, phase, p);
if (JS2VAL_IS_PRIMITIVE(retval))
reportError(Exception::badValueError, "Type expected", p->pos);
JS2Object *obj = JS2VAL_TO_OBJECT(retval);
if (obj->kind != ClassKind)
reportError(Exception::badValueError, "Type expected", p->pos);
return checked_cast<JS2Class *>(obj);
}
// Invoke the named function on the thisValue object (it is an object)
// Returns false if no callable function exists. Otherwise return the
// function result value.
bool JS2Metadata::invokeFunctionOnObject(js2val thisValue, const String *fnName, js2val &result)
{
js2val fnVal;
JS2Class *limit = objectType(thisValue);
if (limit->readPublic(this, &thisValue, limit, fnName, RunPhase, &fnVal)) {
if (JS2VAL_IS_OBJECT(fnVal)) {
JS2Object *fnObj = JS2VAL_TO_OBJECT(fnVal);
if ((fnObj->kind == SimpleInstanceKind)
&& ((checked_cast<SimpleInstance *>(fnObj))->type == functionClass)) {
result = invokeFunction(fnObj, thisValue, NULL, 0, NULL);
return true;
}
}
}
return false;
}
// Invoke the named function, no args, by looking it up
// the current scope. (Do this by emitting and executing the
// appropriate bytecode sequence)
js2val JS2Metadata::invokeFunction(const char *fname)
{
js2val retval;
uint8 *savePC = NULL;
CompilationData *oldData = startCompilationUnit(NULL, bCon->mSource, bCon->mSourceLocation);
try {
LexicalReference rVal(&world.identifiers[widenCString(fname)], false);
rVal.emitReadForInvokeBytecode(bCon, 0);
bCon->emitOp(eCall, 0, -(0 + 2) + 1); // pop argCount args, the base & function, and push a result
bCon->addShort(0);
bCon->emitOp(eReturn, 0);
savePC = engine->pc;
engine->pc = NULL;
retval = engine->interpret(RunPhase, bCon, env);
}
catch (Exception &x) {
engine->pc = savePC;
restoreCompilationUnit(oldData);
throw x;
}
engine->pc = savePC;
restoreCompilationUnit(oldData);
return retval;
}
// Invoke the constructor function for a class
void JS2Metadata::invokeInit(JS2Class *c, js2val thisValue, js2val *argv, uint32 argc)
{
FunctionInstance *init = NULL;
if (c) init = c->init;
if (init) {
ParameterFrame *runtimeFrame;
DEFINE_ROOTKEEPER(rk, runtimeFrame);
runtimeFrame = new ParameterFrame(init->fWrap->compileFrame);
if (!init->fWrap->compileFrame->callsSuperConstructor) {
invokeInit(c->super, thisValue, NULL, 0);
runtimeFrame->superConstructorCalled = true;
}
invokeFunction(init, thisValue, argv, argc, runtimeFrame);
if (!runtimeFrame->superConstructorCalled)
reportError(Exception::uninitializedError, "The superconstuctor must be called before returning normally from a constructor", engine->errorPos());
}
else
if (argc)
reportError(Exception::argumentsError, "The default constructor does not take any arguments", engine->errorPos());
}
js2val JS2Metadata::invokeFunction(JS2Object *fnObj, js2val thisValue, js2val *argv, uint32 argc, ParameterFrame *runtimeFrame)
{
js2val result = JS2VAL_UNDEFINED;
FunctionWrapper *fWrap = NULL;
if ((fnObj->kind == SimpleInstanceKind)
&& ((checked_cast<SimpleInstance *>(fnObj))->type == functionClass)) {
FunctionInstance *fInst = checked_cast<FunctionInstance *>(fnObj);
fWrap = fInst->fWrap;
if (fInst->isMethodClosure) {
// XXX ignoring fInst->thisObject;
}
if (fWrap->code) {
result = (fWrap->code)(this, thisValue, argv, argc);
}
else {
uint8 *savePC = NULL;
BytecodeContainer *bCon = fWrap->bCon;
CompilationData *oldData = startCompilationUnit(bCon, bCon->mSource, bCon->mSourceLocation);
DEFINE_ROOTKEEPER(rk, runtimeFrame);
if (runtimeFrame == NULL)
runtimeFrame = new ParameterFrame(fWrap->compileFrame);
runtimeFrame->instantiate(fWrap->env);
runtimeFrame->thisObject = thisValue;
runtimeFrame->assignArguments(this, fnObj, argv, argc, argc);
Frame *oldTopFrame = fWrap->env->getTopFrame();
if (fInst->isMethodClosure)
fWrap->env->addFrame(objectType(thisValue));
fWrap->env->addFrame(runtimeFrame);
ParameterFrame *oldPFrame = engine->parameterFrame;
try {
savePC = engine->pc;
engine->pc = NULL;
engine->parameterFrame = runtimeFrame;
result = engine->interpret(RunPhase, bCon, fWrap->env);
}
catch (Exception &x) {
engine->pc = savePC;
restoreCompilationUnit(oldData);
fWrap->env->setTopFrame(oldTopFrame);
engine->parameterFrame = oldPFrame;
throw x;
}
engine->pc = savePC;
restoreCompilationUnit(oldData);
fWrap->env->setTopFrame(oldTopFrame);
engine->parameterFrame = oldPFrame;
}
}
return result;
}
// Save off info about the current compilation and begin a
// new one - using the given parser.
CompilationData *JS2Metadata::startCompilationUnit(BytecodeContainer *newBCon, const String &source, const String &sourceLocation)
{
CompilationData *result = new CompilationData();
result->compilation_bCon = bCon;
result->execution_bCon = engine->bCon;
bConList.push_back(bCon);
if (newBCon)
bCon = newBCon;
else
bCon = new BytecodeContainer();
bCon->mSource = source;
bCon->mSourceLocation = sourceLocation;
engine->bCon = bCon;
return result;
}
// Restore the compilation data, and then delete the cached copy.
void JS2Metadata::restoreCompilationUnit(CompilationData *oldData)
{
BytecodeContainer *xbCon = bConList.back();
ASSERT(oldData->compilation_bCon == xbCon);
bConList.pop_back();
bCon = oldData->compilation_bCon;
engine->bCon = oldData->execution_bCon;
delete oldData;
}
// x is not a String
const String *JS2Metadata::convertValueToString(js2val x)
{
if (JS2VAL_IS_UNDEFINED(x))
return engine->undefined_StringAtom;
if (JS2VAL_IS_NULL(x))
return engine->null_StringAtom;
if (JS2VAL_IS_BOOLEAN(x))
return (JS2VAL_TO_BOOLEAN(x)) ? engine->true_StringAtom : engine->false_StringAtom;
if (JS2VAL_IS_INT(x))
return engine->numberToString(JS2VAL_TO_INT(x));
if (JS2VAL_IS_LONG(x)) {
float64 d;
JSLL_L2D(d, *JS2VAL_TO_LONG(x));
return engine->numberToString(&d);
}
if (JS2VAL_IS_ULONG(x)) {
float64 d;
JSLL_UL2D(d, *JS2VAL_TO_ULONG(x));
return engine->numberToString(&d);
}
if (JS2VAL_IS_FLOAT(x)) {
float64 d = *JS2VAL_TO_FLOAT(x);
return engine->numberToString(&d);
}
if (JS2VAL_IS_DOUBLE(x))
return engine->numberToString(JS2VAL_TO_DOUBLE(x));
return toString(toPrimitive(x, StringHint));
}
// x is not a primitive (it is an object and not null)
js2val JS2Metadata::convertValueToPrimitive(js2val x, Hint hint)
{
// return [[DefaultValue]] --> get property 'toString' and invoke it,
// if not available or result is not primitive then try property 'valueOf'
// if that's not available or returns a non primitive, throw a TypeError
JS2Object *obj = JS2VAL_TO_OBJECT(x);
if (obj->kind == ClassKind) // therefore, not an E3 object, so just return
return engine->typeofString(x); // the 'typeof' string
if (hint == NoHint) {
if ((obj->kind == SimpleInstanceKind)
&& ((checked_cast<SimpleInstance *>(obj))->type == dateClass))
hint = StringHint;
else
hint = NumberHint;
}
if (hint == StringHint) {
js2val result;
if (invokeFunctionOnObject(x, engine->toString_StringAtom, result)) {
if (JS2VAL_IS_PRIMITIVE(result))
return result;
}
if (invokeFunctionOnObject(x, engine->valueOf_StringAtom, result)) {
if (JS2VAL_IS_PRIMITIVE(result))
return result;
}
reportError(Exception::typeError, "DefaultValue failure", engine->errorPos());
}
else {
js2val result;
if (invokeFunctionOnObject(x, engine->valueOf_StringAtom, result)) {
if (JS2VAL_IS_PRIMITIVE(result))
return result;
}
if (invokeFunctionOnObject(x, engine->toString_StringAtom, result)) {
if (JS2VAL_IS_PRIMITIVE(result))
return result;
}
reportError(Exception::typeError, "DefaultValue failure", engine->errorPos());
}
return JS2VAL_VOID;
}
float64 JS2Metadata::convertStringToDouble(const String *str)
{
bool neg = false;
uint32 length = str->length();
if (length == 0)
return 0.0;
const char16 *numEnd;
// if the string begins with '0X' or '0x' (after white space), then
// read it as a hex integer.
const char16 *strStart = str->data();
const char16 *strEnd = strStart + length;
const char16 *str1 = skipWhiteSpace(strStart, strEnd);
if (str1 == strEnd)
return 0.0;
if (*str1 == '-') {
neg = true;
str1++;
}
float64 d;
if ((*str1 == '0') && ((str1[1] == 'x') || (str1[1] == 'X')))
d = stringToInteger(str1, strEnd, numEnd, 16);
else {
d = stringToDouble(str1, strEnd, numEnd);
if (numEnd == str1)
return nan;
if (skipWhiteSpace(numEnd, strEnd) != strEnd)
return nan;
}
return (neg) ? -d : d;
}
// x is not a number
float64 JS2Metadata::convertValueToDouble(js2val x)
{
if (JS2VAL_IS_UNDEFINED(x))
return nan;
if (JS2VAL_IS_NULL(x))
return 0;
if (JS2VAL_IS_BOOLEAN(x))
return (JS2VAL_TO_BOOLEAN(x)) ? 1.0 : 0.0;
if (JS2VAL_IS_STRING(x)) {
String *str = JS2VAL_TO_STRING(x);
return convertStringToDouble(str);
}
if (JS2VAL_IS_INACCESSIBLE(x))
reportError(Exception::compileExpressionError, "Inappropriate compile time expression", engine->errorPos());
if (JS2VAL_IS_UNINITIALIZED(x))
reportError(Exception::compileExpressionError, "Inappropriate compile time expression", engine->errorPos());
return toFloat64(toPrimitive(x, NumberHint));
}
// x is not a number, convert it to one
js2val JS2Metadata::convertValueToGeneralNumber(js2val x)
{
// XXX Assuming convert to float64, rather than long/ulong
return engine->allocNumber(toFloat64(x));
}
// x is not an Object, it needs to be wrapped in one
js2val JS2Metadata::convertValueToObject(js2val x)
{
if (JS2VAL_IS_UNDEFINED(x) || JS2VAL_IS_NULL(x) || JS2VAL_IS_SPECIALREF(x))
reportError(Exception::typeError, "Can't convert to Object", engine->errorPos());
if (JS2VAL_IS_STRING(x))
return String_Constructor(this, JS2VAL_NULL, &x, 1);
if (JS2VAL_IS_BOOLEAN(x))
return Boolean_Constructor(this, JS2VAL_NULL, &x, 1);
if (JS2VAL_IS_NUMBER(x))
return Number_Constructor(this, JS2VAL_NULL, &x, 1);
NOT_REACHED("unsupported value type");
return JS2VAL_VOID;
}
// x is any js2val
float64 JS2Metadata::toFloat64(js2val x)
{
if (JS2VAL_IS_INT(x))
return JS2VAL_TO_INT(x);
else
if (JS2VAL_IS_DOUBLE(x))
return *JS2VAL_TO_DOUBLE(x);
else
if (JS2VAL_IS_LONG(x)) {
float64 d;
JSLL_L2D(d, *JS2VAL_TO_LONG(x));
return d;
}
else
if (JS2VAL_IS_ULONG(x)) {
float64 d;
JSLL_UL2D(d, *JS2VAL_TO_ULONG(x));
return d;
}
else
if (JS2VAL_IS_FLOAT(x))
return *JS2VAL_TO_FLOAT(x);
else
return convertValueToDouble(x);
}
// x is not a bool
bool JS2Metadata::convertValueToBoolean(js2val x)
{
if (JS2VAL_IS_UNDEFINED(x))
return false;
if (JS2VAL_IS_NULL(x))
return false;
if (JS2VAL_IS_INT(x))
return (JS2VAL_TO_INT(x) != 0);
if (JS2VAL_IS_LONG(x) || JS2VAL_IS_ULONG(x))
return (!JSLL_IS_ZERO(x));
if (JS2VAL_IS_FLOAT(x)) {
float64 xd = *JS2VAL_TO_FLOAT(x);
return ! (JSDOUBLE_IS_POSZERO(xd) || JSDOUBLE_IS_NEGZERO(xd) || JSDOUBLE_IS_NaN(xd));
}
if (JS2VAL_IS_DOUBLE(x)) {
float64 xd = *JS2VAL_TO_DOUBLE(x);
return ! (JSDOUBLE_IS_POSZERO(xd) || JSDOUBLE_IS_NEGZERO(xd) || JSDOUBLE_IS_NaN(xd));
}
if (JS2VAL_IS_STRING(x)) {
String *str = JS2VAL_TO_STRING(x);
return (str->length() != 0);
}
return true;
}
// x is not an int
int32 JS2Metadata::convertValueToInteger(js2val x)
{
int32 i;
if (JS2VAL_IS_LONG(x)) {
JSLL_L2I(i, *JS2VAL_TO_LONG(x));
return i;
}
if (JS2VAL_IS_ULONG(x)) {
JSLL_UL2I(i, *JS2VAL_TO_ULONG(x));
return i;
}
if (JS2VAL_IS_FLOAT(x)) {
float64 f = *JS2VAL_TO_FLOAT(x);
return JS2Engine::float64toInt32(f);
}
if (JS2VAL_IS_DOUBLE(x)) {
float64 d = *JS2VAL_TO_DOUBLE(x);
return JS2Engine::float64toInt32(d);
}
float64 d = convertValueToDouble(x);
return JS2Engine::float64toInt32(d);
}
// x is not an int
uint32 JS2Metadata::convertValueToUInteger(js2val x)
{
uint32 i;
if (JS2VAL_IS_LONG(x)) {
JSLL_L2UI(i, *JS2VAL_TO_LONG(x));
return i;
}
if (JS2VAL_IS_ULONG(x)) {
JSLL_UL2UI(i, *JS2VAL_TO_ULONG(x));
return i;
}
if (JS2VAL_IS_FLOAT(x)) {
float64 f = *JS2VAL_TO_FLOAT(x);
return JS2Engine::float64toUInt32(f);
}
if (JS2VAL_IS_DOUBLE(x)) {
float64 d = *JS2VAL_TO_DOUBLE(x);
return JS2Engine::float64toUInt32(d);
}
float64 d = convertValueToDouble(x);
return JS2Engine::float64toUInt32(d);
}
bool defaultReadProperty(JS2Metadata *meta, js2val *base, JS2Class *limit, Multiname *multiname, Environment *env, Phase phase, js2val *rval)
{
InstanceMember *mBase = meta->findBaseInstanceMember(limit, multiname, ReadAccess);
if (mBase)
return meta->readInstanceMember(*base, limit, mBase, phase, rval);
if (limit != meta->objectType(*base))
return false;
Member *m = meta->findCommonMember(base, multiname, ReadAccess, false);
if (m == NULL) {
if ((env == NULL)
&& JS2VAL_IS_OBJECT(*base)
&& (( (JS2VAL_TO_OBJECT(*base)->kind == SimpleInstanceKind) && !checked_cast<SimpleInstance *>(JS2VAL_TO_OBJECT(*base))->sealed)
|| ( (JS2VAL_TO_OBJECT(*base)->kind == PackageKind) && !checked_cast<Package *>(JS2VAL_TO_OBJECT(*base))->sealed) ) ) {
if (phase == CompilePhase)
meta->reportError(Exception::compileExpressionError, "Inappropriate compile time expression", meta->engine->errorPos());
else {
*rval = JS2VAL_UNDEFINED;
return true;
}
}
else
return false;
}
switch (m->memberKind) {
case Member::ForbiddenMember:
case Member::DynamicVariableMember:
case Member::FrameVariableMember:
case Member::VariableMember:
case Member::ConstructorMethodMember:
case Member::SetterMember:
case Member::GetterMember:
return meta->readLocalMember(checked_cast<LocalMember *>(m), phase, rval);
case Member::InstanceVariableMember:
case Member::InstanceMethodMember:
case Member::InstanceGetterMember:
case Member::InstanceSetterMember:
{
if (!JS2VAL_IS_OBJECT(*base) || (JS2VAL_TO_OBJECT(*base)->kind != ClassKind) || (env == NULL))
meta->reportError(Exception::referenceError, "Can't read an instance member without supplying an instance", meta->engine->errorPos());
js2val thisVal = env->readImplicitThis(meta);
return meta->readInstanceMember(thisVal, meta->objectType(thisVal), checked_cast<InstanceMember *>(m), phase, rval);
}
default:
NOT_REACHED("bad member kind");
return false;
}
}
bool defaultReadPublicProperty(JS2Metadata *meta, js2val *base, JS2Class *limit, const String *name, Phase phase, js2val *rval)
{
// XXX could speed up by pushing knowledge of single namespace?
DEFINE_ROOTKEEPER(rk1, name);
Multiname *mn = new Multiname(name, meta->publicNamespace);
DEFINE_ROOTKEEPER(rk, mn);
return defaultReadProperty(meta, base, limit, mn, NULL, phase, rval);
}
bool defaultDeletePublic(JS2Metadata *meta, js2val base, JS2Class *limit, const String *name, bool *result)
{
DEFINE_ROOTKEEPER(rk1, name);
// XXX could speed up by pushing knowledge of single namespace?
Multiname *mn = new Multiname(name, meta->publicNamespace);
DEFINE_ROOTKEEPER(rk, mn);
return defaultDeleteProperty(meta, base, limit, mn, NULL, result);
}
bool defaultWritePublicProperty(JS2Metadata *meta, js2val base, JS2Class *limit, const String *name, bool createIfMissing, js2val newValue)
{
DEFINE_ROOTKEEPER(rk1, name);
// XXX could speed up by pushing knowledge of single namespace?
Multiname *mn = new Multiname(name, meta->publicNamespace);
DEFINE_ROOTKEEPER(rk, mn);
return defaultWriteProperty(meta, base, limit, mn, NULL, createIfMissing, newValue, false);
}
bool defaultBracketRead(JS2Metadata *meta, js2val *base, JS2Class *limit, js2val indexVal, Phase phase, js2val *rval)
{
const String *indexStr = meta->toString(indexVal);
DEFINE_ROOTKEEPER(rk, indexStr);
Multiname *mn = new Multiname(indexStr, meta->publicNamespace);
DEFINE_ROOTKEEPER(rk1, mn);
return limit->read(meta, base, limit, mn, NULL, phase, rval);
}
bool arrayClass_WriteProperty(JS2Metadata *meta, js2val base, JS2Class *limit, Multiname *multiname, Environment *env, bool createIfMissing, js2val newValue, bool initFlag)
{
ASSERT(JS2VAL_IS_OBJECT(base));
JS2Object *obj = JS2VAL_TO_OBJECT(base);
bool result = defaultWriteProperty(meta, base, limit, multiname, env, createIfMissing, newValue, false);
if (result && (multiname->nsList->size() == 1) && (multiname->nsList->back() == meta->publicNamespace)) {
const char16 *numEnd;
float64 f = stringToDouble(multiname->name->data(), multiname->name->data() + multiname->name->length(), numEnd);
uint32 index = JS2Engine::float64toUInt32(f);
char buf[dtosStandardBufferSize];
const char *chrp = doubleToStr(buf, dtosStandardBufferSize, index, dtosStandard, 0);
if (widenCString(chrp) == *multiname->name) {
uint32 length = getLength(meta, obj);
if (index >= length)
setLength(meta, obj, index + 1);
}
}
return result;
}
bool arrayClass_WritePublic(JS2Metadata *meta, js2val base, JS2Class *limit, const String *name, bool createIfMissing, js2val newValue)
{
DEFINE_ROOTKEEPER(rk1, name);
// XXX could speed up by pushing knowledge of single namespace?
Multiname *mn = new Multiname(name, meta->publicNamespace);
DEFINE_ROOTKEEPER(rk, mn);
return arrayClass_WriteProperty(meta, base, limit, mn, meta->env, createIfMissing, newValue, false);
}
bool defaultWriteProperty(JS2Metadata *meta, js2val base, JS2Class *limit, Multiname *multiname, Environment *env, bool createIfMissing, js2val newValue, bool initFlag)
{
InstanceMember *mBase = meta->findBaseInstanceMember(limit, multiname, WriteAccess);
if (mBase) {
meta->writeInstanceMember(base, limit, mBase, newValue);
return true;
}
if (limit != meta->objectType(base))
return false;
Member *m = meta->findCommonMember(&base, multiname, WriteAccess, true);
if (m == NULL) {
// XXX E3 compatibility...
JS2Object *baseObj = NULL;
DEFINE_ROOTKEEPER(rk, baseObj);
if (JS2VAL_IS_PRIMITIVE(base)) {
if (meta->cxt.E3compatibility)
baseObj = JS2VAL_TO_OBJECT(meta->toObject(base));
}
else
baseObj = JS2VAL_TO_OBJECT(base);
if (createIfMissing
&& baseObj
&& ( ((baseObj->kind == SimpleInstanceKind) && !checked_cast<SimpleInstance *>(baseObj)->sealed)
|| ( (baseObj->kind == PackageKind) && !checked_cast<Package *>(baseObj)->sealed)) ) {
QualifiedName qName = multiname->selectPrimaryName(meta);
Multiname *mn = new Multiname(qName);
DEFINE_ROOTKEEPER(rk, mn);
if ( (meta->findBaseInstanceMember(limit, mn, ReadAccess) == NULL)
&& (meta->findCommonMember(&base, mn, ReadAccess, true) == NULL) ) {
meta->createDynamicProperty(baseObj, &qName, newValue, ReadWriteAccess, false, true);
return true;
}
}
return false;
}
switch (m->memberKind) {
case Member::ForbiddenMember:
case Member::DynamicVariableMember:
case Member::FrameVariableMember:
case Member::VariableMember:
case Member::ConstructorMethodMember:
case Member::SetterMember:
case Member::GetterMember:
return meta->writeLocalMember(checked_cast<LocalMember *>(m), newValue, initFlag);
case Member::InstanceVariableMember:
case Member::InstanceMethodMember:
case Member::InstanceGetterMember:
case Member::InstanceSetterMember:
{
if ( !JS2VAL_IS_OBJECT(base) || (JS2VAL_TO_OBJECT(base)->kind != ClassKind) || (env == NULL))
meta->reportError(Exception::referenceError, "Can't write an instance member withoutsupplying an instance", meta->engine->errorPos());
js2val thisVal = env->readImplicitThis(meta);
meta->writeInstanceMember(thisVal, meta->objectType(thisVal), checked_cast<InstanceMember *>(m), newValue);
return true;
}
default:
NOT_REACHED("bad member kind");
return false;
}
}
bool defaultBracketWrite(JS2Metadata *meta, js2val base, JS2Class *limit, js2val indexVal, js2val newValue)
{
const String *indexStr = meta->toString(indexVal);
DEFINE_ROOTKEEPER(rk, indexStr);
Multiname *mn = new Multiname(indexStr, meta->publicNamespace);
DEFINE_ROOTKEEPER(rk1, mn);
return limit->write(meta, base, limit, mn, NULL, true, newValue, false);
}
bool defaultDeleteProperty(JS2Metadata *meta, js2val base, JS2Class *limit, Multiname *multiname, Environment *env, bool *result)
{
InstanceMember *mBase = meta->findBaseInstanceMember(limit, multiname, WriteAccess);
if (mBase) {
*result = false;
return true;
}
if (limit != meta->objectType(base))
return false;
Member *m = meta->findCommonMember(&base, multiname, WriteAccess, false);
if (m == NULL)
return false;
switch (m->memberKind) {
case Member::ForbiddenMember:
meta->reportError(Exception::propertyAccessError, "It is forbidden", meta->engine->errorPos());
return false;
case Member::FrameVariableMember:
{
if (checked_cast<FrameVariable *>(m)->sealed) {
*result = false;
return true;
}
goto VariableMemberCommon;
}
case Member::DynamicVariableMember:
{
if (checked_cast<DynamicVariable *>(m)->sealed) {
*result = false;
return true;
}
VariableMemberCommon:
// XXX if findCommonMember returned the Binding instead, we wouldn't have to rediscover it here...
JS2Object *container = JS2VAL_TO_OBJECT(meta->toObject(base));
LocalBindingMap *lMap;
if (container->kind == SimpleInstanceKind)
lMap = &checked_cast<SimpleInstance *>(container)->localBindings;
else
lMap = &checked_cast<NonWithFrame *>(container)->localBindings;
LocalBindingEntry **lbeP = (*lMap)[*multiname->name];
if (lbeP) {
while (true) {
bool deletedOne = false;
for (LocalBindingEntry::NS_Iterator i = (*lbeP)->begin(), end = (*lbeP)->end(); (i != end); i++) {
LocalBindingEntry::NamespaceBinding &ns = *i;
if (multiname->listContains(ns.first)) {
(*lbeP)->bindingList.erase(i);
deletedOne = true;
break;
}
}
if (!deletedOne)
break;
}
}
*result = true;
return true;
}
case Member::VariableMember:
case Member::ConstructorMethodMember:
case Member::SetterMember:
case Member::GetterMember:
*result = false;
return true;
case Member::InstanceVariableMember:
case Member::InstanceMethodMember:
case Member::InstanceGetterMember:
case Member::InstanceSetterMember:
if ( (!JS2VAL_IS_OBJECT(base) || (JS2VAL_TO_OBJECT(base)->kind != ClassKind)) || (env == NULL)) {
*result = false;
return true;
}
env->readImplicitThis(meta);
*result = false;
return true;
default:
NOT_REACHED("bad member kind");
return false;
}
}
bool defaultBracketDelete(JS2Metadata *meta, js2val base, JS2Class *limit, js2val indexVal, bool *result)
{
const String *indexStr = meta->toString(indexVal);
DEFINE_ROOTKEEPER(rk, indexStr);
Multiname *mn = new Multiname(indexStr, meta->publicNamespace);
DEFINE_ROOTKEEPER(rk1, mn);
return limit->deleteProperty(meta, base, limit, mn, NULL, result);
}
js2val defaultImplicitCoerce(JS2Metadata *meta, js2val newValue, JS2Class *isClass)
{
if (JS2VAL_IS_NULL(newValue) || meta->objectType(newValue)->isAncestor(isClass) )
return newValue;
meta->reportError(Exception::badValueError, "Illegal coercion", meta->engine->errorPos());
return JS2VAL_VOID;
}
js2val integerImplicitCoerce(JS2Metadata *meta, js2val newValue, JS2Class *isClass)
{
if (JS2VAL_IS_UNDEFINED(newValue))
return JS2VAL_ZERO;
if (JS2VAL_IS_NUMBER(newValue)) {
int64 x = meta->engine->checkInteger(newValue);
if (JSLL_IS_INT32(x)) {
int32 i = 0;
JSLL_L2I(i, x);
return INT_TO_JS2VAL(i);
}
}
meta->reportError(Exception::badValueError, "Illegal coercion", meta->engine->errorPos());
return JS2VAL_VOID;
}
js2val defaultIs(JS2Metadata *meta, js2val newValue, JS2Class *isClass)
{
return BOOLEAN_TO_JS2VAL(meta->objectType(newValue) == isClass);
}
js2val integerIs(JS2Metadata *meta, js2val newValue, JS2Class *isClass)
{
bool result = false;
if (JS2VAL_IS_NUMBER(newValue)) {
float64 f = meta->toFloat64(newValue);
if (JSDOUBLE_IS_FINITE(f)) {
}
}
return BOOLEAN_TO_JS2VAL(result);
}
bool stringClass_BracketRead(JS2Metadata *meta, js2val *base, JS2Class *limit, js2val indexVal, Phase phase, js2val *rval)
{
if (JS2VAL_IS_INT(indexVal)) {
const String *str = NULL;
if (JS2VAL_IS_STRING(*base)) {
str = JS2VAL_TO_STRING(*base);
}
else {
ASSERT(JS2VAL_IS_OBJECT(*base));
JS2Object *obj = JS2VAL_TO_OBJECT(*base);
ASSERT((obj->kind == SimpleInstanceKind) && (checked_cast<SimpleInstance *>(obj)->type == meta->stringClass));
StringInstance *a = checked_cast<StringInstance *>(obj);
str = a->mValue;
}
int32 i = JS2VAL_TO_INT(indexVal);
if ((i >= 0) && (i < str->length()))
*rval = meta->engine->allocString(&(*str)[i], 1);
else
*rval = JS2VAL_UNDEFINED;
return true;
}
else
return defaultBracketRead(meta, base, limit, indexVal, phase, rval);
}
}; // namespace MetaData
}; // namespace Javascript