# HG changeset patch # User Asher Baker # Date 1370822683 -3600 # Node ID ba85a47ee414f5526f53d7983f72267500d6c8ad # Parent c4b1341297e5654475e9318f2f7ed0ba5288b3ee Normalise line endings. diff -r c4b1341297e5 -r ba85a47ee414 jitcraft.sln --- a/jitcraft.sln Mon Jun 10 00:46:29 2013 +0100 +++ b/jitcraft.sln Mon Jun 10 01:04:43 2013 +0100 @@ -1,23 +1,23 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual C++ Express 2010 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jitcraft", "src\jitcraft.vcxproj", "{E12034DC-E8CB-4B49-AC0A-0D27CDED9A50}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug - Library|Win32 = Debug - Library|Win32 - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {E12034DC-E8CB-4B49-AC0A-0D27CDED9A50}.Debug - Library|Win32.ActiveCfg = Debug - Library|Win32 - {E12034DC-E8CB-4B49-AC0A-0D27CDED9A50}.Debug - Library|Win32.Build.0 = Debug - Library|Win32 - {E12034DC-E8CB-4B49-AC0A-0D27CDED9A50}.Debug|Win32.ActiveCfg = Debug|Win32 - {E12034DC-E8CB-4B49-AC0A-0D27CDED9A50}.Debug|Win32.Build.0 = Debug|Win32 - {E12034DC-E8CB-4B49-AC0A-0D27CDED9A50}.Release|Win32.ActiveCfg = Release|Win32 - {E12034DC-E8CB-4B49-AC0A-0D27CDED9A50}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual C++ Express 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jitcraft", "src\jitcraft.vcxproj", "{E12034DC-E8CB-4B49-AC0A-0D27CDED9A50}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug - Library|Win32 = Debug - Library|Win32 + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E12034DC-E8CB-4B49-AC0A-0D27CDED9A50}.Debug - Library|Win32.ActiveCfg = Debug - Library|Win32 + {E12034DC-E8CB-4B49-AC0A-0D27CDED9A50}.Debug - Library|Win32.Build.0 = Debug - Library|Win32 + {E12034DC-E8CB-4B49-AC0A-0D27CDED9A50}.Debug|Win32.ActiveCfg = Debug|Win32 + {E12034DC-E8CB-4B49-AC0A-0D27CDED9A50}.Debug|Win32.Build.0 = Debug|Win32 + {E12034DC-E8CB-4B49-AC0A-0D27CDED9A50}.Release|Win32.ActiveCfg = Release|Win32 + {E12034DC-E8CB-4B49-AC0A-0D27CDED9A50}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff -r c4b1341297e5 -r ba85a47ee414 product.version --- a/product.version Mon Jun 10 00:46:29 2013 +0100 +++ b/product.version Mon Jun 10 01:04:43 2013 +0100 @@ -1,1 +1,1 @@ -0.0.2-alpha +0.0.2-alpha diff -r c4b1341297e5 -r ba85a47ee414 src/Array.cpp --- a/src/Array.cpp Mon Jun 10 00:46:29 2013 +0100 +++ b/src/Array.cpp Mon Jun 10 01:04:43 2013 +0100 @@ -1,378 +1,378 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#include -#include "Array.h" -#include "FixedArray.h" -#include "Heap.h" -#include "Zone.h" -#include "Structures.h" -#include "Heap-inl.h" - -using namespace ke; - -ArrayMap * -ArrayMap::New(Zone *zone, Handle type) -{ - Local obj(zone, zone->allocateMap(MapKind_Array, sizeof(ArrayMap), MapKind_MetaArray)); - if (!obj) - return NULL; - - Local map(zone, ArrayMap::cast(obj)); - map->type_ = type; - return map; -} - -ArrayMap * -ArrayMap::NewDependent(Zone *zone, Handle type) -{ - Local obj(zone, zone->allocateMap(MapKind_DependentArray, sizeof(ArrayMap), MapKind_MetaArray)); - if (!obj) - return NULL; - - Local map(zone, ArrayMap::cast(obj)); - map->type_ = type; - return map; -} - -Array * -Array::NewRaw(Zone *zone, Handle map, unsigned nelements, Heap::Tenure tenure) -{ - assert(!map->type()->isFixedLength() || nelements == map->type()->fixedLength()); - - if (nelements > (INT_MAX / SizeForElement(map->type()))) { - zone->reportError(Message_ArraySizeTooBig); - return NULL; - } - - size_t bytes = sizeof(Array); - if (map->type()->hasInlineElements()) - bytes += SizeForElement(map->type()) * nelements; - - Local array(zone, Array::cast(zone->allocate(map, bytes, tenure))); - if (!array) - return NULL; - - array->length_ = 0; - array->elements_ = NULL; - - if (map->type()->hasInlineElements()) { - // This must be a fixed array containing no traceables. - assert(nelements > 0); - assert(map->type()->isFixedLength()); - assert(!map->type()->contained()->isTraceable()); - - // Compute an interior |elements_| pointer, such that if elements_ - // is accessed like a FixedArray, we reach the appropriate vector. - array->elements_ = Address((Array *)array + 1) - ByteArray::offsetToElements(); - array->length_ = nelements; - memset(array->bytes(), 0, SizeForElement(map->type()) * nelements); - return array; - } - - // If there is nothing to allocate, we don't bother creating elements yet. - if (!nelements) - return array; - - if (!map->type()->contained()->isTraceable()) { - size_t nbytes = SizeForElement(map->type()) * nelements; - Local elements(zone, ByteArray::New(zone, nbytes, tenure)); - if (!elements) - return NULL; - - array->updateLengthAndElements(zone, nelements, elements); - return array; - } - - Local elements(zone, FixedArray::New(zone, nelements, tenure)); - if (!elements) - return NULL; - - array->updateLengthAndElements(zone, nelements, elements); - - // If this is an array of structs, we're required to allocate those structs now. - if (map->type()->contained()->isStruct()) { - Local childType(zone, StructType::cast(map->type()->contained())); - Local childMap(zone, childType->newMap()); - Local child(zone); - for (unsigned i = 0; i < nelements; i++) { - child = Struct::New(zone, childMap, tenure); - if (!child) - return NULL; - - elements->set(zone, i, child); - } - } - - return array; -} - -static Array * -ConstructSizedArray(Zone *zone, Handle map, int dims[MAX_ARRAY_DEPTH], - unsigned level, Heap::Tenure tenure) -{ - Local array(zone, Array::NewRaw(zone, map, dims[level], tenure)); - if (!array) - return NULL; - - if (map->type()->contained()->isArray()) { - Local childType(zone, ArrayType::cast(map->type()->contained())); - Local childMap(zone, childType->newMap()); - Local child(zone); - - for (unsigned i = 0; i < unsigned(dims[level]); i++) { - child = ConstructSizedArray(zone, childMap, dims, level + 1, tenure); - if (!child) - return NULL; - - array->writeObject(zone, i, child); - } - } - - return array; -} - -Array * -Array::NewSized(Zone *zone, Handle map, int dims[MAX_ARRAY_DEPTH], Heap::Tenure tenure) -{ - for (size_t i = 0; i < map->type()->levels(); i++) { - if (dims[i] < 0) { - zone->reportError(Message_InvalidDimensionSize, dims[i], i); - return NULL; - } - } - - return ConstructSizedArray(zone, map, dims, 0, tenure); -} - -static Array * -ConstructFixedArray(Zone *zone, Handle map, Heap::Tenure tenure) -{ - unsigned length = map->type()->fixedLength(); - Local array(zone, Array::NewRaw(zone, map, length, tenure)); - if (!array) - return NULL; - - if (map->type()->contained()->isArray()) { - Local childType(zone, ArrayType::cast(map->type()->contained())); - Local childMap(zone, childType->newMap()); - Local child(zone); - - if (childMap->type()->isFixedLength()) { - for (unsigned i = 0; i < length; i++) { - child = ConstructFixedArray(zone, childMap, tenure); - if (!child) - return NULL; - - array->writeObject(zone, i, child); - } - } else { - for (unsigned i = 0; i < length; i++) { - child = Array::NewRaw(zone, childMap, 0, tenure); - if (!child) - return NULL; - - array->writeObject(zone, i, child); - } - } - } - - return array; -} - -Array * -Array::NewFixed(Zone *zone, Handle map, Heap::Tenure tenure) -{ - return ConstructFixedArray(zone, map, tenure); -} - -Array * -Array::NewString(Zone *zone, Handle map, char *buffer, size_t length, Heap::Tenure tenure) -{ - assert(map->type()->isCharArray()); - - Local array(zone); - - if (map->type()->isFixedLength()) - array = Array::NewFixed(zone, map, tenure); - else - array = Array::New1D(zone, map, length + 1, tenure); - if (!array) - return NULL; - - assert(array->length() >= length + 1); - memcpy(array->chars(), buffer, length + 1); - return array; -} - -Array * -Array::New1D(Zone *zone, Handle map, uint32 elements, Heap::Tenure tenure) -{ - assert(map->type()->levels()); - return Array::NewRaw(zone, map, elements, tenure); -} - -Array * -Array::NewEmpty(Zone *zone, Handle map, Heap::Tenure tenure) -{ - assert(!map->type()->isFixedLength()); - - return Array::NewRaw(zone, map, 0, tenure); -} - -Array * -Array::NewExternal(Zone *zone, Handle map, void *buffer, unsigned length, - Heap::Tenure tenure) -{ - Local array(zone, Array::cast(zone->allocate(map, sizeof(Array), tenure))); - if (!array) - return NULL; - - array->length_ = length; - array->elements_ = Address(buffer) - ByteArray::offsetToElements(); - return array; -} - -static bool -CopyArrayElements(Zone *zone, Handle contained, Handle dest, Handle source) -{ - if (contained->isTraceable()) { - if (contained->isStruct()) { - assert(dest->type()->contained() == contained); - assert(source->length() == dest->length()); - - Local left(zone); - Local right(zone); - for (unsigned i = 0; i < source->length(); i++) { - left = Struct::cast(dest->readObject(i)); - right = Struct::cast(source->readObject(i)); - if (!Struct::Copy(zone, left, right)) - return false; - } - } else if (contained->isArray() && ArrayType::cast(contained)->isFixedLength()) { - assert(ArrayType::cast(source->type())->fixedLength() == - ArrayType::cast(contained)->fixedLength()); - - Local left(zone); - Local right(zone); - for (unsigned i = 0; i < source->length(); i++) { - left = Array::cast(dest->readObject(i)); - right = Array::cast(source->readObject(i)); - if (!Array::ShallowCopy(zone, left, right)) - return false; - } - } else { - assert(dest->length() == source->length()); - for (unsigned i = 0; i < source->length(); i++) - dest->writeObject(zone, i, source->readObject(i)); - } - } else { - assert(contained->pod() == dest->type()->contained()->pod()); - assert(contained->pod() == PrimitiveType_Char - ? source->length() <= dest->length() - : source->length() == dest->length()); - - assert(dest->length()); - memcpy(dest->bytes(), source->bytes(), source->length() * Array::SizeForElement(dest->type())); - } - return true; -} - -// Shallow copy an array. Fixed slots in the destination are copied from -// the source, but otherwise, references are assigned. -bool -Array::ShallowCopy(Zone *zone, Handle dest, Handle source) -{ - Local contained(zone, dest->type()->contained()); - - return CopyArrayElements(zone, contained, dest, source); -} - -Array * -Array::DeepCopy(Zone *zone, Handle source, Heap::Tenure tenure) -{ - Local map(zone, ArrayMap::cast(source->map())); - - Local dest(zone, Array::NewRaw(zone, map, source->length(), tenure)); - if (!dest) - return NULL; - - Local contained(zone, source->type()->contained()); - if (contained->isArray()) { - assert(false); - Local sub(zone); - for (size_t i = 0; i < source->length(); i++) { - sub = Array::cast(source->readObject(i)); - sub = DeepCopy(zone, sub, tenure); - if (!sub) - return NULL; - dest->writeObject(zone, i, sub); - } - } else { - if (!CopyArrayElements(zone, contained, dest, source)) - return NULL; - } - - return dest; -} - -Array * -DependentArray::New(Zone *zone, Handle source, Handle map, unsigned index) -{ - Local sourcetype(zone, source->type()); - - // Right now we only support char arrays, to avoid nasty deep cloning - // and to maybe cut off this feature as far as we can. - assert(sourcetype->isCharArray()); - assert(map->type()->isCharArray()); - - // We allow taking 0-length dependent strings. It's safe because any reads - // or writes will fail. - assert(index <= source->length()); - - unsigned newlen = source->length() - index; - unsigned eltsize = SizeForElement(sourcetype); - - if (!sourcetype->isFixedLength()) { - // Simple case, just copy the array. - Local map(zone, sourcetype->newMap()); - - Local dest(zone, Array::NewRaw(zone, map, newlen, Heap::Tenure_Default)); - if (!dest) - return NULL; - - memcpy(dest->bytes(), - (Address)source->bytes() + (index * eltsize), - newlen * eltsize); - return dest; - } - - Local array(zone, DependentArray::cast(zone->allocate(map, sizeof(DependentArray), Heap::Tenure_Default))); - if (!array) - return NULL; - - // :GC: This is currently not safe for moving GC! Although we will mark - // the object owning the interior pointer, the interior pointer could be - // moved later. We will eventually need some way of dealing with this. - Address elements = Address(source->bytes()) + (index * eltsize); - - array->root_ = source; - array->length_ = newlen; - array->elements_ = elements - ByteArray::offsetToElements(); - return array; -} +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#include +#include "Array.h" +#include "FixedArray.h" +#include "Heap.h" +#include "Zone.h" +#include "Structures.h" +#include "Heap-inl.h" + +using namespace ke; + +ArrayMap * +ArrayMap::New(Zone *zone, Handle type) +{ + Local obj(zone, zone->allocateMap(MapKind_Array, sizeof(ArrayMap), MapKind_MetaArray)); + if (!obj) + return NULL; + + Local map(zone, ArrayMap::cast(obj)); + map->type_ = type; + return map; +} + +ArrayMap * +ArrayMap::NewDependent(Zone *zone, Handle type) +{ + Local obj(zone, zone->allocateMap(MapKind_DependentArray, sizeof(ArrayMap), MapKind_MetaArray)); + if (!obj) + return NULL; + + Local map(zone, ArrayMap::cast(obj)); + map->type_ = type; + return map; +} + +Array * +Array::NewRaw(Zone *zone, Handle map, unsigned nelements, Heap::Tenure tenure) +{ + assert(!map->type()->isFixedLength() || nelements == map->type()->fixedLength()); + + if (nelements > (INT_MAX / SizeForElement(map->type()))) { + zone->reportError(Message_ArraySizeTooBig); + return NULL; + } + + size_t bytes = sizeof(Array); + if (map->type()->hasInlineElements()) + bytes += SizeForElement(map->type()) * nelements; + + Local array(zone, Array::cast(zone->allocate(map, bytes, tenure))); + if (!array) + return NULL; + + array->length_ = 0; + array->elements_ = NULL; + + if (map->type()->hasInlineElements()) { + // This must be a fixed array containing no traceables. + assert(nelements > 0); + assert(map->type()->isFixedLength()); + assert(!map->type()->contained()->isTraceable()); + + // Compute an interior |elements_| pointer, such that if elements_ + // is accessed like a FixedArray, we reach the appropriate vector. + array->elements_ = Address((Array *)array + 1) - ByteArray::offsetToElements(); + array->length_ = nelements; + memset(array->bytes(), 0, SizeForElement(map->type()) * nelements); + return array; + } + + // If there is nothing to allocate, we don't bother creating elements yet. + if (!nelements) + return array; + + if (!map->type()->contained()->isTraceable()) { + size_t nbytes = SizeForElement(map->type()) * nelements; + Local elements(zone, ByteArray::New(zone, nbytes, tenure)); + if (!elements) + return NULL; + + array->updateLengthAndElements(zone, nelements, elements); + return array; + } + + Local elements(zone, FixedArray::New(zone, nelements, tenure)); + if (!elements) + return NULL; + + array->updateLengthAndElements(zone, nelements, elements); + + // If this is an array of structs, we're required to allocate those structs now. + if (map->type()->contained()->isStruct()) { + Local childType(zone, StructType::cast(map->type()->contained())); + Local childMap(zone, childType->newMap()); + Local child(zone); + for (unsigned i = 0; i < nelements; i++) { + child = Struct::New(zone, childMap, tenure); + if (!child) + return NULL; + + elements->set(zone, i, child); + } + } + + return array; +} + +static Array * +ConstructSizedArray(Zone *zone, Handle map, int dims[MAX_ARRAY_DEPTH], + unsigned level, Heap::Tenure tenure) +{ + Local array(zone, Array::NewRaw(zone, map, dims[level], tenure)); + if (!array) + return NULL; + + if (map->type()->contained()->isArray()) { + Local childType(zone, ArrayType::cast(map->type()->contained())); + Local childMap(zone, childType->newMap()); + Local child(zone); + + for (unsigned i = 0; i < unsigned(dims[level]); i++) { + child = ConstructSizedArray(zone, childMap, dims, level + 1, tenure); + if (!child) + return NULL; + + array->writeObject(zone, i, child); + } + } + + return array; +} + +Array * +Array::NewSized(Zone *zone, Handle map, int dims[MAX_ARRAY_DEPTH], Heap::Tenure tenure) +{ + for (size_t i = 0; i < map->type()->levels(); i++) { + if (dims[i] < 0) { + zone->reportError(Message_InvalidDimensionSize, dims[i], i); + return NULL; + } + } + + return ConstructSizedArray(zone, map, dims, 0, tenure); +} + +static Array * +ConstructFixedArray(Zone *zone, Handle map, Heap::Tenure tenure) +{ + unsigned length = map->type()->fixedLength(); + Local array(zone, Array::NewRaw(zone, map, length, tenure)); + if (!array) + return NULL; + + if (map->type()->contained()->isArray()) { + Local childType(zone, ArrayType::cast(map->type()->contained())); + Local childMap(zone, childType->newMap()); + Local child(zone); + + if (childMap->type()->isFixedLength()) { + for (unsigned i = 0; i < length; i++) { + child = ConstructFixedArray(zone, childMap, tenure); + if (!child) + return NULL; + + array->writeObject(zone, i, child); + } + } else { + for (unsigned i = 0; i < length; i++) { + child = Array::NewRaw(zone, childMap, 0, tenure); + if (!child) + return NULL; + + array->writeObject(zone, i, child); + } + } + } + + return array; +} + +Array * +Array::NewFixed(Zone *zone, Handle map, Heap::Tenure tenure) +{ + return ConstructFixedArray(zone, map, tenure); +} + +Array * +Array::NewString(Zone *zone, Handle map, char *buffer, size_t length, Heap::Tenure tenure) +{ + assert(map->type()->isCharArray()); + + Local array(zone); + + if (map->type()->isFixedLength()) + array = Array::NewFixed(zone, map, tenure); + else + array = Array::New1D(zone, map, length + 1, tenure); + if (!array) + return NULL; + + assert(array->length() >= length + 1); + memcpy(array->chars(), buffer, length + 1); + return array; +} + +Array * +Array::New1D(Zone *zone, Handle map, uint32 elements, Heap::Tenure tenure) +{ + assert(map->type()->levels()); + return Array::NewRaw(zone, map, elements, tenure); +} + +Array * +Array::NewEmpty(Zone *zone, Handle map, Heap::Tenure tenure) +{ + assert(!map->type()->isFixedLength()); + + return Array::NewRaw(zone, map, 0, tenure); +} + +Array * +Array::NewExternal(Zone *zone, Handle map, void *buffer, unsigned length, + Heap::Tenure tenure) +{ + Local array(zone, Array::cast(zone->allocate(map, sizeof(Array), tenure))); + if (!array) + return NULL; + + array->length_ = length; + array->elements_ = Address(buffer) - ByteArray::offsetToElements(); + return array; +} + +static bool +CopyArrayElements(Zone *zone, Handle contained, Handle dest, Handle source) +{ + if (contained->isTraceable()) { + if (contained->isStruct()) { + assert(dest->type()->contained() == contained); + assert(source->length() == dest->length()); + + Local left(zone); + Local right(zone); + for (unsigned i = 0; i < source->length(); i++) { + left = Struct::cast(dest->readObject(i)); + right = Struct::cast(source->readObject(i)); + if (!Struct::Copy(zone, left, right)) + return false; + } + } else if (contained->isArray() && ArrayType::cast(contained)->isFixedLength()) { + assert(ArrayType::cast(source->type())->fixedLength() == + ArrayType::cast(contained)->fixedLength()); + + Local left(zone); + Local right(zone); + for (unsigned i = 0; i < source->length(); i++) { + left = Array::cast(dest->readObject(i)); + right = Array::cast(source->readObject(i)); + if (!Array::ShallowCopy(zone, left, right)) + return false; + } + } else { + assert(dest->length() == source->length()); + for (unsigned i = 0; i < source->length(); i++) + dest->writeObject(zone, i, source->readObject(i)); + } + } else { + assert(contained->pod() == dest->type()->contained()->pod()); + assert(contained->pod() == PrimitiveType_Char + ? source->length() <= dest->length() + : source->length() == dest->length()); + + assert(dest->length()); + memcpy(dest->bytes(), source->bytes(), source->length() * Array::SizeForElement(dest->type())); + } + return true; +} + +// Shallow copy an array. Fixed slots in the destination are copied from +// the source, but otherwise, references are assigned. +bool +Array::ShallowCopy(Zone *zone, Handle dest, Handle source) +{ + Local contained(zone, dest->type()->contained()); + + return CopyArrayElements(zone, contained, dest, source); +} + +Array * +Array::DeepCopy(Zone *zone, Handle source, Heap::Tenure tenure) +{ + Local map(zone, ArrayMap::cast(source->map())); + + Local dest(zone, Array::NewRaw(zone, map, source->length(), tenure)); + if (!dest) + return NULL; + + Local contained(zone, source->type()->contained()); + if (contained->isArray()) { + assert(false); + Local sub(zone); + for (size_t i = 0; i < source->length(); i++) { + sub = Array::cast(source->readObject(i)); + sub = DeepCopy(zone, sub, tenure); + if (!sub) + return NULL; + dest->writeObject(zone, i, sub); + } + } else { + if (!CopyArrayElements(zone, contained, dest, source)) + return NULL; + } + + return dest; +} + +Array * +DependentArray::New(Zone *zone, Handle source, Handle map, unsigned index) +{ + Local sourcetype(zone, source->type()); + + // Right now we only support char arrays, to avoid nasty deep cloning + // and to maybe cut off this feature as far as we can. + assert(sourcetype->isCharArray()); + assert(map->type()->isCharArray()); + + // We allow taking 0-length dependent strings. It's safe because any reads + // or writes will fail. + assert(index <= source->length()); + + unsigned newlen = source->length() - index; + unsigned eltsize = SizeForElement(sourcetype); + + if (!sourcetype->isFixedLength()) { + // Simple case, just copy the array. + Local map(zone, sourcetype->newMap()); + + Local dest(zone, Array::NewRaw(zone, map, newlen, Heap::Tenure_Default)); + if (!dest) + return NULL; + + memcpy(dest->bytes(), + (Address)source->bytes() + (index * eltsize), + newlen * eltsize); + return dest; + } + + Local array(zone, DependentArray::cast(zone->allocate(map, sizeof(DependentArray), Heap::Tenure_Default))); + if (!array) + return NULL; + + // :GC: This is currently not safe for moving GC! Although we will mark + // the object owning the interior pointer, the interior pointer could be + // moved later. We will eventually need some way of dealing with this. + Address elements = Address(source->bytes()) + (index * eltsize); + + array->root_ = source; + array->length_ = newlen; + array->elements_ = elements - ByteArray::offsetToElements(); + return array; +} diff -r c4b1341297e5 -r ba85a47ee414 src/Array.h --- a/src/Array.h Mon Jun 10 00:46:29 2013 +0100 +++ b/src/Array.h Mon Jun 10 01:04:43 2013 +0100 @@ -1,244 +1,244 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#ifndef _include_jitcraft_array_h_ -#define _include_jitcraft_array_h_ - -#include "Heap.h" -#include "Handles.h" -#include "Types.h" -#include "FixedArray.h" - -namespace ke { - -class ArrayMap; - -class Array : public Object -{ - friend class ArrayMap; - - protected: - // Note: we do not write barrier |elements_|, since it can be set to - // arbitrary pointers. - uint32 length_; - union { - void *elements_; - Object *objelements_; - }; - - protected: - void updateLengthAndElements(Zone *zone, unsigned length, Object *array) { - length_ = length; - write(zone, &objelements_, array); - } - - public: - static Array *NewFixed(Zone *zone, Handle map, Heap::Tenure = Heap::Tenure_Default); - static Array *NewString(Zone *zone, Handle map, char *buffer, size_t length, Heap::Tenure = Heap::Tenure_Default); - static Array *NewSized(Zone *zone, Handle map, int sizes[MAX_ARRAY_DEPTH], Heap::Tenure = Heap::Tenure_Default); - - static Array *New1D(Zone *zone, Handle map, uint32 elements, Heap::Tenure tenure = Heap::Tenure_Default); - static Array *NewEmpty(Zone *zone, Handle map, Heap::Tenure tenure = Heap::Tenure_Default); - static Array *NewRaw(Zone *zone, Handle map, unsigned nelements, Heap::Tenure tenure); - - static Array *NewExternal(Zone *zone, - Handle map, - void *buffer, - unsigned length, - Heap::Tenure tenure = Heap::Tenure_Default); - - // Copies an array from one reference to another, obeying language semantics. - static bool ShallowCopy(Zone *zone, Handle dest, Handle source); - - // Creates a full duplicate of an array, following all array references as if - // they were fixed. Array references inside structs or objects use ShallowCopy - // semantics, as this is only intended for duplicating array literals, which - // cannot contain structs. - static Array *DeepCopy(Zone *zone, Handle source, Heap::Tenure tenure); - - uint32 length() { - return length_; - } - - inline ArrayType *type(); - - inline int *int32Vec(); - inline float *floatVec(); - inline char *chars(); - inline void *bytes(); - - bool resize(Zone *zone, size_t newLength); - - static inline size_t SizeForElement(ArrayType *type) { - if (type->contained()->isTraceable()) - return sizeof(ke::Object *); - if (type->contained()->isInt32()) - return sizeof(int); - if (type->contained()->primitive() == PrimitiveType_Char) - return sizeof(char); - assert(type->contained()->isFloat()); - return sizeof(float); - } - - inline size_t objSize(ArrayMap *map); - - int readInt32(size_t index) { - assert(index < length()); - return int32Vec()[index]; - } - void writeInt32(size_t index, int i) { - assert(index < length()); - int32Vec()[index] = i; - } - char readChar(size_t index) { - assert(index < length()); - return chars()[index]; - } - void writeChar(size_t index, char c) { - assert(index < length()); - chars()[index] = c; - } - float readFloat(size_t index) { - assert(index < length()); - return floatVec()[index]; - } - void writeFloat(size_t index, float f) { - assert(index < length()); - floatVec()[index] = f; - } - Object *readObject(size_t index) { - return FixedArray::cast(objelements_)->at(index); - } - void writeObject(Zone *zone, size_t index, Object *ptr) { - FixedArray::cast(objelements_)->set(zone, index, ptr); - } - - // GC Descriptor. - static inline size_t offsetOfElements() { - return offsetof(Array, objelements_); - } - // End GC Descriptor. - - static Array *cast(Object *obj) { - assert(!obj || - obj->is(MapKind_Array) || - obj->is(MapKind_DependentArray)); - return reinterpret_cast(obj); - } -}; - -class DependentArray : public Array -{ - Barriered root_; - - public: - static Array *New(Zone *zone, Handle source, Handle map, unsigned index); - - // GC Descriptor. - static inline size_t offsetOfRoot() { - return offsetof(DependentArray, root_); - } - // End GC Descriptor. - - static DependentArray *cast(Object *obj) { - assert(!obj || obj->is(MapKind_DependentArray)); - return reinterpret_cast(obj); - } -}; - -class ArrayMap : public Map -{ - Barriered type_; - - public: - static ArrayMap *New(Zone *zone, Handle type); - - // DependentArray maps are never attached, and are just free-floating, - // rooted ultimately by the bytecode. A :TODO: that we might need to - // cache these, or attach them somewhere saner like ArrayType, which - // itself will eventually be cached. - static ArrayMap *NewDependent(Zone *zone, Handle type); - - ArrayType *type() { - return type_; - } - - // GC Descriptor. - static inline size_t offsetOfType() { - return OFFSETOF(ArrayMap, type_); - } - // End GC Descriptor. - - static ArrayMap *cast(Object *obj) { - assert(!obj || obj->is(MapKind_MetaArray)); - return reinterpret_cast(obj); - } -}; - -inline void * -Array::bytes() -{ - assert(!type()->contained()->isTraceable()); - assert(type()->hasInlineElements() || - type()->isExternalArray() || - is(MapKind_DependentArray) || - !length() || - ByteArray::cast((Object *)elements_)); - - return Address(elements_) + ByteArray::offsetToElements(); -} - -inline int * -Array::int32Vec() -{ - assert(type()->contained()->isInt32()); - return reinterpret_cast(bytes()); -} - -inline float * -Array::floatVec() -{ - assert(type()->contained()->isFloat()); - return reinterpret_cast(bytes()); -} - -inline char * -Array::chars() -{ - assert(type()->isCharArray()); - return reinterpret_cast(bytes()); -} - -inline ArrayType * -Array::type() -{ - return ArrayMap::cast(map())->type(); -} - -inline size_t -Array::objSize(ArrayMap *map) -{ - assert(this->map() == map); - if (map->type()->hasInlineElements()) - return sizeof(Array) + Array::SizeForElement(map->type()) * length(); - return sizeof(Array); -} - -} - -#endif // _include_jitcraft_array_h_ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_array_h_ +#define _include_jitcraft_array_h_ + +#include "Heap.h" +#include "Handles.h" +#include "Types.h" +#include "FixedArray.h" + +namespace ke { + +class ArrayMap; + +class Array : public Object +{ + friend class ArrayMap; + + protected: + // Note: we do not write barrier |elements_|, since it can be set to + // arbitrary pointers. + uint32 length_; + union { + void *elements_; + Object *objelements_; + }; + + protected: + void updateLengthAndElements(Zone *zone, unsigned length, Object *array) { + length_ = length; + write(zone, &objelements_, array); + } + + public: + static Array *NewFixed(Zone *zone, Handle map, Heap::Tenure = Heap::Tenure_Default); + static Array *NewString(Zone *zone, Handle map, char *buffer, size_t length, Heap::Tenure = Heap::Tenure_Default); + static Array *NewSized(Zone *zone, Handle map, int sizes[MAX_ARRAY_DEPTH], Heap::Tenure = Heap::Tenure_Default); + + static Array *New1D(Zone *zone, Handle map, uint32 elements, Heap::Tenure tenure = Heap::Tenure_Default); + static Array *NewEmpty(Zone *zone, Handle map, Heap::Tenure tenure = Heap::Tenure_Default); + static Array *NewRaw(Zone *zone, Handle map, unsigned nelements, Heap::Tenure tenure); + + static Array *NewExternal(Zone *zone, + Handle map, + void *buffer, + unsigned length, + Heap::Tenure tenure = Heap::Tenure_Default); + + // Copies an array from one reference to another, obeying language semantics. + static bool ShallowCopy(Zone *zone, Handle dest, Handle source); + + // Creates a full duplicate of an array, following all array references as if + // they were fixed. Array references inside structs or objects use ShallowCopy + // semantics, as this is only intended for duplicating array literals, which + // cannot contain structs. + static Array *DeepCopy(Zone *zone, Handle source, Heap::Tenure tenure); + + uint32 length() { + return length_; + } + + inline ArrayType *type(); + + inline int *int32Vec(); + inline float *floatVec(); + inline char *chars(); + inline void *bytes(); + + bool resize(Zone *zone, size_t newLength); + + static inline size_t SizeForElement(ArrayType *type) { + if (type->contained()->isTraceable()) + return sizeof(ke::Object *); + if (type->contained()->isInt32()) + return sizeof(int); + if (type->contained()->primitive() == PrimitiveType_Char) + return sizeof(char); + assert(type->contained()->isFloat()); + return sizeof(float); + } + + inline size_t objSize(ArrayMap *map); + + int readInt32(size_t index) { + assert(index < length()); + return int32Vec()[index]; + } + void writeInt32(size_t index, int i) { + assert(index < length()); + int32Vec()[index] = i; + } + char readChar(size_t index) { + assert(index < length()); + return chars()[index]; + } + void writeChar(size_t index, char c) { + assert(index < length()); + chars()[index] = c; + } + float readFloat(size_t index) { + assert(index < length()); + return floatVec()[index]; + } + void writeFloat(size_t index, float f) { + assert(index < length()); + floatVec()[index] = f; + } + Object *readObject(size_t index) { + return FixedArray::cast(objelements_)->at(index); + } + void writeObject(Zone *zone, size_t index, Object *ptr) { + FixedArray::cast(objelements_)->set(zone, index, ptr); + } + + // GC Descriptor. + static inline size_t offsetOfElements() { + return offsetof(Array, objelements_); + } + // End GC Descriptor. + + static Array *cast(Object *obj) { + assert(!obj || + obj->is(MapKind_Array) || + obj->is(MapKind_DependentArray)); + return reinterpret_cast(obj); + } +}; + +class DependentArray : public Array +{ + Barriered root_; + + public: + static Array *New(Zone *zone, Handle source, Handle map, unsigned index); + + // GC Descriptor. + static inline size_t offsetOfRoot() { + return offsetof(DependentArray, root_); + } + // End GC Descriptor. + + static DependentArray *cast(Object *obj) { + assert(!obj || obj->is(MapKind_DependentArray)); + return reinterpret_cast(obj); + } +}; + +class ArrayMap : public Map +{ + Barriered type_; + + public: + static ArrayMap *New(Zone *zone, Handle type); + + // DependentArray maps are never attached, and are just free-floating, + // rooted ultimately by the bytecode. A :TODO: that we might need to + // cache these, or attach them somewhere saner like ArrayType, which + // itself will eventually be cached. + static ArrayMap *NewDependent(Zone *zone, Handle type); + + ArrayType *type() { + return type_; + } + + // GC Descriptor. + static inline size_t offsetOfType() { + return OFFSETOF(ArrayMap, type_); + } + // End GC Descriptor. + + static ArrayMap *cast(Object *obj) { + assert(!obj || obj->is(MapKind_MetaArray)); + return reinterpret_cast(obj); + } +}; + +inline void * +Array::bytes() +{ + assert(!type()->contained()->isTraceable()); + assert(type()->hasInlineElements() || + type()->isExternalArray() || + is(MapKind_DependentArray) || + !length() || + ByteArray::cast((Object *)elements_)); + + return Address(elements_) + ByteArray::offsetToElements(); +} + +inline int * +Array::int32Vec() +{ + assert(type()->contained()->isInt32()); + return reinterpret_cast(bytes()); +} + +inline float * +Array::floatVec() +{ + assert(type()->contained()->isFloat()); + return reinterpret_cast(bytes()); +} + +inline char * +Array::chars() +{ + assert(type()->isCharArray()); + return reinterpret_cast(bytes()); +} + +inline ArrayType * +Array::type() +{ + return ArrayMap::cast(map())->type(); +} + +inline size_t +Array::objSize(ArrayMap *map) +{ + assert(this->map() == map); + if (map->type()->hasInlineElements()) + return sizeof(Array) + Array::SizeForElement(map->type()) * length(); + return sizeof(Array); +} + +} + +#endif // _include_jitcraft_array_h_ diff -r c4b1341297e5 -r ba85a47ee414 src/Code.h --- a/src/Code.h Mon Jun 10 00:46:29 2013 +0100 +++ b/src/Code.h Mon Jun 10 01:04:43 2013 +0100 @@ -1,140 +1,140 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of JITCraft. - * - * JITCraft is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * JITCraft. If not, see http://www.gnu.org/licenses/. - */ -#ifndef _include_jc_code_h_ -#define _include_jc_code_h_ - -#include "Opcodes.h" -#include "Barriers.h" -#include "Heap.h" -#include "Types.h" -#include "FixedArray.h" - -namespace ke { - -class Module; -class Type; - -// A code object contains the bytecode compiled from a function (or global -// script), as well as all information needed to interpret or JIT-compile -// the bytecode. -class Code : public Object -{ - Barriered module_; - Barriered bytecode_; - Barriered locals_; - Barriered objects_; - Barriered strings_; - Barriered safepoints_; - Barriered sourcemap_; - unsigned maxStackDepth_; - unsigned lineNumber_; - unsigned maxLoopDepth_; - - public: - static inline size_t BytesPerSafepoint(size_t stackDepth) { - return (Align(stackDepth, 8) / 8) + sizeof(unsigned); - } - - public: - static Code *New(Zone *zone, Handle bytecode, Handle locals, - Handle objects, Handle strings, unsigned maxStackDepth, - unsigned maxLoopDepth, Handle safepoints, Handle sourcemap); - - Module *module() { - return module_; - } - bytecode *start() { - return bytecode_->raw(); - } - unsigned length() { - return bytecode_->length(); - } - Type *localType(size_t index) { - return Type::cast(locals_->at(index)); - } - unsigned nlocals() { - return locals_ ? locals_->length() : 0; - } - unsigned maxStackDepth() { - return maxStackDepth_; - } - unsigned maxLoopDepth() { - return maxLoopDepth_; - } - Map *map(size_t index) { - return Map::cast(objects_->at(index)); - } - Object *object(size_t index) { - return objects_->at(index); - } - ByteArray *safepoints() { - return safepoints_; - } - FixedArray *strings() { - return strings_; - } - uint8 *safepointAt(unsigned pcOffset); - - void setModule(Handle module) { - module_ = module; - } - - unsigned pcToOffset(bytecode *pc) { - assert(pc >= start() && pc < start() + length()); - return unsigned(pc - start()); - } - - SourcePosition pcToSource(unsigned pc); - unsigned lineNumber() { - return lineNumber_; - } - - // GC Descriptor. - static inline size_t offsetOfModule() { - return OFFSETOF(Code, module_); - } - static inline size_t offsetOfBytecode() { - return OFFSETOF(Code, bytecode_); - } - static inline size_t offsetOfLocals() { - return OFFSETOF(Code, locals_); - } - static inline size_t offsetOfObjects() { - return OFFSETOF(Code, objects_); - } - static inline size_t offsetOfStrings() { - return OFFSETOF(Code, strings_); - } - static inline size_t offsetOfSafepoints() { - return OFFSETOF(Code, safepoints_); - } - static inline size_t offsetOfSourceMap() { - return OFFSETOF(Code, sourcemap_); - } - // End GC Descriptor. - - static Code *cast(Object *obj) { - assert(!obj || obj->is(MapKind_Code)); - return reinterpret_cast(obj); - } -}; - -} - -#endif // _include_jc_code_h_ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jc_code_h_ +#define _include_jc_code_h_ + +#include "Opcodes.h" +#include "Barriers.h" +#include "Heap.h" +#include "Types.h" +#include "FixedArray.h" + +namespace ke { + +class Module; +class Type; + +// A code object contains the bytecode compiled from a function (or global +// script), as well as all information needed to interpret or JIT-compile +// the bytecode. +class Code : public Object +{ + Barriered module_; + Barriered bytecode_; + Barriered locals_; + Barriered objects_; + Barriered strings_; + Barriered safepoints_; + Barriered sourcemap_; + unsigned maxStackDepth_; + unsigned lineNumber_; + unsigned maxLoopDepth_; + + public: + static inline size_t BytesPerSafepoint(size_t stackDepth) { + return (Align(stackDepth, 8) / 8) + sizeof(unsigned); + } + + public: + static Code *New(Zone *zone, Handle bytecode, Handle locals, + Handle objects, Handle strings, unsigned maxStackDepth, + unsigned maxLoopDepth, Handle safepoints, Handle sourcemap); + + Module *module() { + return module_; + } + bytecode *start() { + return bytecode_->raw(); + } + unsigned length() { + return bytecode_->length(); + } + Type *localType(size_t index) { + return Type::cast(locals_->at(index)); + } + unsigned nlocals() { + return locals_ ? locals_->length() : 0; + } + unsigned maxStackDepth() { + return maxStackDepth_; + } + unsigned maxLoopDepth() { + return maxLoopDepth_; + } + Map *map(size_t index) { + return Map::cast(objects_->at(index)); + } + Object *object(size_t index) { + return objects_->at(index); + } + ByteArray *safepoints() { + return safepoints_; + } + FixedArray *strings() { + return strings_; + } + uint8 *safepointAt(unsigned pcOffset); + + void setModule(Handle module) { + module_ = module; + } + + unsigned pcToOffset(bytecode *pc) { + assert(pc >= start() && pc < start() + length()); + return unsigned(pc - start()); + } + + SourcePosition pcToSource(unsigned pc); + unsigned lineNumber() { + return lineNumber_; + } + + // GC Descriptor. + static inline size_t offsetOfModule() { + return OFFSETOF(Code, module_); + } + static inline size_t offsetOfBytecode() { + return OFFSETOF(Code, bytecode_); + } + static inline size_t offsetOfLocals() { + return OFFSETOF(Code, locals_); + } + static inline size_t offsetOfObjects() { + return OFFSETOF(Code, objects_); + } + static inline size_t offsetOfStrings() { + return OFFSETOF(Code, strings_); + } + static inline size_t offsetOfSafepoints() { + return OFFSETOF(Code, safepoints_); + } + static inline size_t offsetOfSourceMap() { + return OFFSETOF(Code, sourcemap_); + } + // End GC Descriptor. + + static Code *cast(Object *obj) { + assert(!obj || obj->is(MapKind_Code)); + return reinterpret_cast(obj); + } +}; + +} + +#endif // _include_jc_code_h_ diff -r c4b1341297e5 -r ba85a47ee414 src/CompactBuffer.h --- a/src/CompactBuffer.h Mon Jun 10 00:46:29 2013 +0100 +++ b/src/CompactBuffer.h Mon Jun 10 01:04:43 2013 +0100 @@ -1,100 +1,100 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of JITCraft. - * - * JITCraft is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * JITCraft. If not, see http://www.gnu.org/licenses/. - */ -#ifndef _include_jc_compact_buffer_h_ -#define _include_jc_compact_buffer_h_ - -#include "AllocatorPolicies.h" -#include "Utility.h" -#include "Vector.h" - -namespace ke { - -class CompactBufferReader -{ - const uint8 *pos_; - const uint8 *end_; - - public: - CompactBufferReader(const uint8 *start, const uint8 *end) - : pos_(start), - end_(end) - { - } - - uint8 readUint8() { - assert(pos_ < end_); - return *pos_++; - } - - uint32 readUint32() { - uint32 value = 0; - uint32 shift = 0; - - uint8 byte; - do { - byte = readUint8(); - value |= (byte >> 1) << shift; - shift += 7; - } while (byte & 1); - - assert(shift < 32); - return value; - } - - bool done() const { - return pos_ == end_; - } -}; - -class CompactBufferWriter -{ - Vector buffer_; - bool enoughMemory_; - - public: - CompactBufferWriter() - : enoughMemory_(true) - { - } - - void writeUint8(unsigned value) { - enoughMemory_ &= buffer_.append(value); - } - void writeUint32(unsigned value) { - do { - writeUint8(((value & 0x7F) << 1) | (value > 0x7F)); - value >>= 7; - } while (value); - } - - uint8 *buffer() const { - return buffer_.buffer(); - } - size_t length() const { - return buffer_.length(); - } - - bool oom() const { - return !enoughMemory_; - } -}; - -} - -#endif // _include_jc_compact_buffer_h_ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jc_compact_buffer_h_ +#define _include_jc_compact_buffer_h_ + +#include "AllocatorPolicies.h" +#include "Utility.h" +#include "Vector.h" + +namespace ke { + +class CompactBufferReader +{ + const uint8 *pos_; + const uint8 *end_; + + public: + CompactBufferReader(const uint8 *start, const uint8 *end) + : pos_(start), + end_(end) + { + } + + uint8 readUint8() { + assert(pos_ < end_); + return *pos_++; + } + + uint32 readUint32() { + uint32 value = 0; + uint32 shift = 0; + + uint8 byte; + do { + byte = readUint8(); + value |= (byte >> 1) << shift; + shift += 7; + } while (byte & 1); + + assert(shift < 32); + return value; + } + + bool done() const { + return pos_ == end_; + } +}; + +class CompactBufferWriter +{ + Vector buffer_; + bool enoughMemory_; + + public: + CompactBufferWriter() + : enoughMemory_(true) + { + } + + void writeUint8(unsigned value) { + enoughMemory_ &= buffer_.append(value); + } + void writeUint32(unsigned value) { + do { + writeUint8(((value & 0x7F) << 1) | (value > 0x7F)); + value >>= 7; + } while (value); + } + + uint8 *buffer() const { + return buffer_.buffer(); + } + size_t length() const { + return buffer_.length(); + } + + bool oom() const { + return !enoughMemory_; + } +}; + +} + +#endif // _include_jc_compact_buffer_h_ diff -r c4b1341297e5 -r ba85a47ee414 src/Environments.cpp --- a/src/Environments.cpp Mon Jun 10 00:46:29 2013 +0100 +++ b/src/Environments.cpp Mon Jun 10 01:04:43 2013 +0100 @@ -1,102 +1,102 @@ -/* - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#include -#include "Utility.h" -#include "Environments.h" -#include "Structures.h" -#include "FixedArray.h" -#include "Zone.h" -#include "compiler/NameBinding.h" -#include "Spaces-inl.h" -#include "Heap-inl.h" - -using namespace ke; - -EnvironmentSlotDescriptorArray * -EnvironmentSlotDescriptorArray::New(Zone *zone, unsigned nslots) -{ - size_t bytesNeeded = sizeof(EnvironmentSlotDescriptorArray) + - sizeof(EnvironmentSlotDescriptor) * nslots; - - Local array(zone); - Local map(zone, zone->getMap(MapKind_EnvironmentSlotDescriptorArray)); - - array = EnvironmentSlotDescriptorArray::cast(zone->allocate(map, bytesNeeded, Heap::Tenure_Old)); - if (!array) - return NULL; - - // We zero the array for safety, since we could GC or fail before initialization. - array->length_ = nslots; - memset(array->slots(), 0, sizeof(EnvironmentSlotDescriptor) * nslots); - return array; -} - -Environment * -Environment::New(Zone *zone, Handle map, Heap::Tenure tenure) -{ - size_t bytesNeeded = sizeof(Environment) + map->structureSize(); - Local env(zone, Environment::cast(zone->allocate(map, bytesNeeded, tenure))); - if (!env) - return NULL; - - // Initialize when called from C++, to make sure object slots are NULL. - memset((Object *)env + 1, 0, map->structureSize()); - return env; -} - -#if 0 -EnvironmentMap * -EnvironmentMap::New(Zone *zone, unsigned nvars, Variable **vars) -{ - StructureBufferBuilder structure; - - Local slots(zone); - slots = EnvironmentSlotDescriptorArray::New(zone, nvars); - if (!slots) - return NULL; - - // Build the descriptor table, mapping each slot to an offset and type. - Local type(zone); - for (unsigned i = 0; i < nvars; i++) { - assert(vars[i]->slot() == i); - type = vars[i]->type(); - - EnvironmentSlotDescriptor &desc = slots->slots()[i]; - if (!structure.add(type, &desc.offset)) - return NULL; - - desc.type = type; - } - - // Allocate the actual map now. - size_t bytesNeeded = sizeof(EnvironmentMap) + structure.traceableOffsetCount() * sizeof(unsigned); - - Local map(zone); - map = EnvironmentMap::cast(zone->allocateMap(MapKind_Environment, bytesNeeded, MapKind_MetaEnvironment)); - if (!map) - return NULL; - - // Fill in the map fields, and copy the traceable offsets structure. - map->structureSize_ = structure.structureSize(); - map->traceOffsetCount_ = structure.traceableOffsetCount(); - map->slots_ = slots; - memcpy(const_cast(map->traceOffsets()), structure.offsets(), sizeof(unsigned) * structure.traceableOffsetCount()); - - return map; -} +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#include +#include "Utility.h" +#include "Environments.h" +#include "Structures.h" +#include "FixedArray.h" +#include "Zone.h" +#include "compiler/NameBinding.h" +#include "Spaces-inl.h" +#include "Heap-inl.h" + +using namespace ke; + +EnvironmentSlotDescriptorArray * +EnvironmentSlotDescriptorArray::New(Zone *zone, unsigned nslots) +{ + size_t bytesNeeded = sizeof(EnvironmentSlotDescriptorArray) + + sizeof(EnvironmentSlotDescriptor) * nslots; + + Local array(zone); + Local map(zone, zone->getMap(MapKind_EnvironmentSlotDescriptorArray)); + + array = EnvironmentSlotDescriptorArray::cast(zone->allocate(map, bytesNeeded, Heap::Tenure_Old)); + if (!array) + return NULL; + + // We zero the array for safety, since we could GC or fail before initialization. + array->length_ = nslots; + memset(array->slots(), 0, sizeof(EnvironmentSlotDescriptor) * nslots); + return array; +} + +Environment * +Environment::New(Zone *zone, Handle map, Heap::Tenure tenure) +{ + size_t bytesNeeded = sizeof(Environment) + map->structureSize(); + Local env(zone, Environment::cast(zone->allocate(map, bytesNeeded, tenure))); + if (!env) + return NULL; + + // Initialize when called from C++, to make sure object slots are NULL. + memset((Object *)env + 1, 0, map->structureSize()); + return env; +} + +#if 0 +EnvironmentMap * +EnvironmentMap::New(Zone *zone, unsigned nvars, Variable **vars) +{ + StructureBufferBuilder structure; + + Local slots(zone); + slots = EnvironmentSlotDescriptorArray::New(zone, nvars); + if (!slots) + return NULL; + + // Build the descriptor table, mapping each slot to an offset and type. + Local type(zone); + for (unsigned i = 0; i < nvars; i++) { + assert(vars[i]->slot() == i); + type = vars[i]->type(); + + EnvironmentSlotDescriptor &desc = slots->slots()[i]; + if (!structure.add(type, &desc.offset)) + return NULL; + + desc.type = type; + } + + // Allocate the actual map now. + size_t bytesNeeded = sizeof(EnvironmentMap) + structure.traceableOffsetCount() * sizeof(unsigned); + + Local map(zone); + map = EnvironmentMap::cast(zone->allocateMap(MapKind_Environment, bytesNeeded, MapKind_MetaEnvironment)); + if (!map) + return NULL; + + // Fill in the map fields, and copy the traceable offsets structure. + map->structureSize_ = structure.structureSize(); + map->traceOffsetCount_ = structure.traceableOffsetCount(); + map->slots_ = slots; + memcpy(const_cast(map->traceOffsets()), structure.offsets(), sizeof(unsigned) * structure.traceableOffsetCount()); + + return map; +} #endif \ No newline at end of file diff -r c4b1341297e5 -r ba85a47ee414 src/Environments.h --- a/src/Environments.h Mon Jun 10 00:46:29 2013 +0100 +++ b/src/Environments.h Mon Jun 10 01:04:43 2013 +0100 @@ -1,169 +1,169 @@ -/* - * Copyright (C) 2012 David Anderson - * - * This file is part of JITCraft. - * - * JITCraft is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * JITCraft. If not, see http://www.gnu.org/licenses/. - */ -#ifndef _include_ke_environment_h_ -#define _include_ke_environment_h_ - -#include "Objects.h" -#include "Handles.h" -#include "Heap.h" -#include "FixedArray.h" - -namespace ke { - -class EnvironmentMap; -class Variable; -class ByteArray; -class Type; - -// An environment represents the lexical environment of a code or function -// object. It used for variables which may be accessed outside a function's -// normal activation (i.e., upvars). -// -// A module uses an Environment to store its global variables. -class Environment : public Object -{ - template - inline T *pointerToSlot(unsigned slot); - - public: - static Environment *New(Zone *zone, Handle map, Heap::Tenure tenure = Heap::Tenure_Default); - - inline EnvironmentMap *map(); - inline size_t objSize(); - - float getFloat(unsigned slot) { - return *pointerToSlot(slot); - } - int getInt32(unsigned slot) { - return *pointerToSlot(slot); - } - Object *getObject(unsigned slot) { - return *pointerToSlot(slot); - } - void setFloat(unsigned slot, float f) { - *pointerToSlot(slot) = f; - } - void setInt32(unsigned slot, int i) { - *pointerToSlot(slot) = i; - } - void setObject(unsigned slot, Object *obj) { - // :TODO: write barrier - *pointerToSlot(slot) = obj; - } - - static inline size_t offsetOfSlotOffset(unsigned offset) { - return sizeof(Environment) + offset; - } - - static inline Environment *cast(Object *obj) { - assert(!obj || obj->is(MapKind_Environment)); - return reinterpret_cast(obj); - } -}; - -struct EnvironmentSlotDescriptor -{ - Barriered type; - unsigned offset; -}; - -class EnvironmentSlotDescriptorArray : public Object -{ - unsigned length_; - - public: - static EnvironmentSlotDescriptorArray *New(Zone *zone, unsigned length); - - EnvironmentSlotDescriptor *slots() { - return reinterpret_cast(this + 1); - } - EnvironmentSlotDescriptor &at(unsigned slot) { - assert(slot < length_); - return slots()[slot]; - } - unsigned length() { - return length_; - } - unsigned objSize() { - return sizeof(*this) + length() * sizeof(EnvironmentSlotDescriptorArray); - } - - static inline EnvironmentSlotDescriptorArray *cast(Object *obj) { - assert(!obj || obj->is(MapKind_EnvironmentSlotDescriptorArray)); - return reinterpret_cast(obj); - } -}; - -class EnvironmentMap : public Map -{ - unsigned structureSize_; - unsigned traceOffsetCount_; - Barriered slots_; - - public: - static EnvironmentMap *New(Zone *zone, unsigned nvars, Variable **vars); - - unsigned structureSize() { - return structureSize_; - } - unsigned traceOffsetCount() { - return traceOffsetCount_; - } - const unsigned *traceOffsets() { - return reinterpret_cast(this + 1); - } - size_t objSize() { - return sizeof(EnvironmentMap) + sizeof(unsigned) * traceOffsetCount(); - } - EnvironmentSlotDescriptor &slotDescriptor(unsigned slot) { - return slots_->at(slot); - } - - // Begin GC descriptors. - static inline size_t offsetOfSlots() { - return OFFSETOF(EnvironmentMap, slots_); - } - // End GC descriptors. - - static inline EnvironmentMap *cast(Object *obj) { - assert(!obj || obj->is(MapKind_MetaEnvironment)); - return reinterpret_cast(obj); - } -}; - -size_t -Environment::objSize() -{ - return sizeof(Environment) + map()->structureSize(); -} - -EnvironmentMap * -Environment::map() -{ - return EnvironmentMap::cast(Object::map()); -} - -template T * -Environment::pointerToSlot(unsigned slot) -{ - return reinterpret_cast(reinterpret_cast(this + 1) + map()->slotDescriptor(slot).offset); -} - -} - -#endif // _include_ke_environment_h_ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_ke_environment_h_ +#define _include_ke_environment_h_ + +#include "Objects.h" +#include "Handles.h" +#include "Heap.h" +#include "FixedArray.h" + +namespace ke { + +class EnvironmentMap; +class Variable; +class ByteArray; +class Type; + +// An environment represents the lexical environment of a code or function +// object. It used for variables which may be accessed outside a function's +// normal activation (i.e., upvars). +// +// A module uses an Environment to store its global variables. +class Environment : public Object +{ + template + inline T *pointerToSlot(unsigned slot); + + public: + static Environment *New(Zone *zone, Handle map, Heap::Tenure tenure = Heap::Tenure_Default); + + inline EnvironmentMap *map(); + inline size_t objSize(); + + float getFloat(unsigned slot) { + return *pointerToSlot(slot); + } + int getInt32(unsigned slot) { + return *pointerToSlot(slot); + } + Object *getObject(unsigned slot) { + return *pointerToSlot(slot); + } + void setFloat(unsigned slot, float f) { + *pointerToSlot(slot) = f; + } + void setInt32(unsigned slot, int i) { + *pointerToSlot(slot) = i; + } + void setObject(unsigned slot, Object *obj) { + // :TODO: write barrier + *pointerToSlot(slot) = obj; + } + + static inline size_t offsetOfSlotOffset(unsigned offset) { + return sizeof(Environment) + offset; + } + + static inline Environment *cast(Object *obj) { + assert(!obj || obj->is(MapKind_Environment)); + return reinterpret_cast(obj); + } +}; + +struct EnvironmentSlotDescriptor +{ + Barriered type; + unsigned offset; +}; + +class EnvironmentSlotDescriptorArray : public Object +{ + unsigned length_; + + public: + static EnvironmentSlotDescriptorArray *New(Zone *zone, unsigned length); + + EnvironmentSlotDescriptor *slots() { + return reinterpret_cast(this + 1); + } + EnvironmentSlotDescriptor &at(unsigned slot) { + assert(slot < length_); + return slots()[slot]; + } + unsigned length() { + return length_; + } + unsigned objSize() { + return sizeof(*this) + length() * sizeof(EnvironmentSlotDescriptorArray); + } + + static inline EnvironmentSlotDescriptorArray *cast(Object *obj) { + assert(!obj || obj->is(MapKind_EnvironmentSlotDescriptorArray)); + return reinterpret_cast(obj); + } +}; + +class EnvironmentMap : public Map +{ + unsigned structureSize_; + unsigned traceOffsetCount_; + Barriered slots_; + + public: + static EnvironmentMap *New(Zone *zone, unsigned nvars, Variable **vars); + + unsigned structureSize() { + return structureSize_; + } + unsigned traceOffsetCount() { + return traceOffsetCount_; + } + const unsigned *traceOffsets() { + return reinterpret_cast(this + 1); + } + size_t objSize() { + return sizeof(EnvironmentMap) + sizeof(unsigned) * traceOffsetCount(); + } + EnvironmentSlotDescriptor &slotDescriptor(unsigned slot) { + return slots_->at(slot); + } + + // Begin GC descriptors. + static inline size_t offsetOfSlots() { + return OFFSETOF(EnvironmentMap, slots_); + } + // End GC descriptors. + + static inline EnvironmentMap *cast(Object *obj) { + assert(!obj || obj->is(MapKind_MetaEnvironment)); + return reinterpret_cast(obj); + } +}; + +size_t +Environment::objSize() +{ + return sizeof(Environment) + map()->structureSize(); +} + +EnvironmentMap * +Environment::map() +{ + return EnvironmentMap::cast(Object::map()); +} + +template T * +Environment::pointerToSlot(unsigned slot) +{ + return reinterpret_cast(reinterpret_cast(this + 1) + map()->slotDescriptor(slot).offset); +} + +} + +#endif // _include_ke_environment_h_ diff -r c4b1341297e5 -r ba85a47ee414 src/Exception.cpp --- a/src/Exception.cpp Mon Jun 10 00:46:29 2013 +0100 +++ b/src/Exception.cpp Mon Jun 10 01:04:43 2013 +0100 @@ -1,31 +1,31 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#include "Utility.h" -#include "Messages.h" - -using namespace ke; - -const MessageInfo ke::Messages[] = -{ -#define MSG(Name, Type, String) \ - { MessageType_##Type, String }, -# include "Messages.tbl" -#undef MSG - { MessageType_SyntaxError, NULL } -}; +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#include "Utility.h" +#include "Messages.h" + +using namespace ke; + +const MessageInfo ke::Messages[] = +{ +#define MSG(Name, Type, String) \ + { MessageType_##Type, String }, +# include "Messages.tbl" +#undef MSG + { MessageType_SyntaxError, NULL } +}; diff -r c4b1341297e5 -r ba85a47ee414 src/FixedArray.cpp --- a/src/FixedArray.cpp Mon Jun 10 00:46:29 2013 +0100 +++ b/src/FixedArray.cpp Mon Jun 10 01:04:43 2013 +0100 @@ -1,78 +1,78 @@ -/* - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#include -#include -#include "FixedArray.h" -#include "Zone.h" -#include "Heap-inl.h" - -using namespace ke; - -static const unsigned kMaxFixedArrayLength = ((INT_MAX - sizeof(FixedArray)) / sizeof(ke::Object *)); - -FixedArray * -FixedArray::New(Zone *zone, unsigned length, Heap::Tenure tenure) -{ - if (length >= kMaxFixedArrayLength) { - zone->reportError(Message_ArrayTooBig); - return NULL; - } - - Local array(zone, FixedArray::cast(zone->allocate(MapKind_FixedArray, SizeFor(length), tenure))); - if (!array) - return NULL; - - array->length_ = length; - memset(array->vec(), 0, length * sizeof(Object *)); - return array; -} - -ByteArray * -ByteArray::New(Zone *zone, unsigned length, Heap::Tenure tenure) -{ - if (length >= kMaxFixedArrayLength) { - zone->reportError(Message_ArrayTooBig); - return NULL; - } - - Local array(zone, ByteArray::cast(zone->allocate(MapKind_ByteArray, SizeFor(length), tenure))); - if (!array) - return NULL; - - array->length_ = length; - memset(array->raw(), 0, length); - return array; -} - -ByteArray * -ByteArray::New(Zone *zone, Handle map, unsigned length, Heap::Tenure tenure) -{ - if (length >= kMaxFixedArrayLength) { - zone->reportError(Message_ArrayTooBig); - return NULL; - } - - Local array(zone, ByteArray::cast(zone->allocate(map, SizeFor(length), tenure))); - if (!array) - return NULL; - - array->length_ = length; - memset(array->raw(), 0, length); - return array; -} - +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#include +#include +#include "FixedArray.h" +#include "Zone.h" +#include "Heap-inl.h" + +using namespace ke; + +static const unsigned kMaxFixedArrayLength = ((INT_MAX - sizeof(FixedArray)) / sizeof(ke::Object *)); + +FixedArray * +FixedArray::New(Zone *zone, unsigned length, Heap::Tenure tenure) +{ + if (length >= kMaxFixedArrayLength) { + zone->reportError(Message_ArrayTooBig); + return NULL; + } + + Local array(zone, FixedArray::cast(zone->allocate(MapKind_FixedArray, SizeFor(length), tenure))); + if (!array) + return NULL; + + array->length_ = length; + memset(array->vec(), 0, length * sizeof(Object *)); + return array; +} + +ByteArray * +ByteArray::New(Zone *zone, unsigned length, Heap::Tenure tenure) +{ + if (length >= kMaxFixedArrayLength) { + zone->reportError(Message_ArrayTooBig); + return NULL; + } + + Local array(zone, ByteArray::cast(zone->allocate(MapKind_ByteArray, SizeFor(length), tenure))); + if (!array) + return NULL; + + array->length_ = length; + memset(array->raw(), 0, length); + return array; +} + +ByteArray * +ByteArray::New(Zone *zone, Handle map, unsigned length, Heap::Tenure tenure) +{ + if (length >= kMaxFixedArrayLength) { + zone->reportError(Message_ArrayTooBig); + return NULL; + } + + Local array(zone, ByteArray::cast(zone->allocate(map, SizeFor(length), tenure))); + if (!array) + return NULL; + + array->length_ = length; + memset(array->raw(), 0, length); + return array; +} + diff -r c4b1341297e5 -r ba85a47ee414 src/FixedArray.h --- a/src/FixedArray.h Mon Jun 10 00:46:29 2013 +0100 +++ b/src/FixedArray.h Mon Jun 10 01:04:43 2013 +0100 @@ -1,99 +1,99 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#ifndef _include_jitcraft_fixedarray_h_ -#define _include_jitcraft_fixedarray_h_ - -#include "Heap.h" - -namespace ke { - -// A fixed-length array that contains Object * slots. -class FixedArray : public Object -{ - uint32 length_; - - private: - Object **vec() { - return reinterpret_cast(this + 1); - } - - public: - static FixedArray *New(Zone *zone, unsigned length, Heap::Tenure tenure); - - unsigned length() { - return length_; - } - Object *at(size_t i) { - assert(i < length()); - return vec()[i]; - } - void set(Zone *zone, size_t i, Object *obj) { - assert(i < length()); - write(zone, &vec()[i], obj); - } - size_t objSize() { - return SizeFor(length()); - } - - static inline size_t SizeFor(unsigned length) { - return sizeof(FixedArray) + sizeof(Object *) * length; - } - - static inline FixedArray *cast(Object *obj) { - assert(!obj || obj->is(MapKind_FixedArray)); - return reinterpret_cast(obj); - } -}; - -// A fixed-length array containing raw bytes. -class ByteArray : public Object -{ - uint32 length_; - - public: - static ByteArray *New(Zone *zone, unsigned length, Heap::Tenure tenure = Heap::Tenure_Default); - static ByteArray *New(Zone *zone, Handle map, unsigned length, Heap::Tenure tenure = Heap::Tenure_Default); - - unsigned length() { - return length_; - } - Address raw() { - return Address(this) + offsetToElements(); - } - size_t objSize() { - return SizeFor(length()); - } - - static inline size_t offsetToElements() { - return sizeof(ByteArray); - } - - static inline size_t SizeFor(unsigned length) { - return sizeof(ByteArray) + sizeof(uint8) * length; - } - - static inline ByteArray *cast(Object *obj) { - assert(!obj || obj->is(MapKind_ByteArray)); - return reinterpret_cast(obj); - } -}; - -} - -#endif // _include_jitcraft_fixedarray_h_ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_fixedarray_h_ +#define _include_jitcraft_fixedarray_h_ + +#include "Heap.h" + +namespace ke { + +// A fixed-length array that contains Object * slots. +class FixedArray : public Object +{ + uint32 length_; + + private: + Object **vec() { + return reinterpret_cast(this + 1); + } + + public: + static FixedArray *New(Zone *zone, unsigned length, Heap::Tenure tenure); + + unsigned length() { + return length_; + } + Object *at(size_t i) { + assert(i < length()); + return vec()[i]; + } + void set(Zone *zone, size_t i, Object *obj) { + assert(i < length()); + write(zone, &vec()[i], obj); + } + size_t objSize() { + return SizeFor(length()); + } + + static inline size_t SizeFor(unsigned length) { + return sizeof(FixedArray) + sizeof(Object *) * length; + } + + static inline FixedArray *cast(Object *obj) { + assert(!obj || obj->is(MapKind_FixedArray)); + return reinterpret_cast(obj); + } +}; + +// A fixed-length array containing raw bytes. +class ByteArray : public Object +{ + uint32 length_; + + public: + static ByteArray *New(Zone *zone, unsigned length, Heap::Tenure tenure = Heap::Tenure_Default); + static ByteArray *New(Zone *zone, Handle map, unsigned length, Heap::Tenure tenure = Heap::Tenure_Default); + + unsigned length() { + return length_; + } + Address raw() { + return Address(this) + offsetToElements(); + } + size_t objSize() { + return SizeFor(length()); + } + + static inline size_t offsetToElements() { + return sizeof(ByteArray); + } + + static inline size_t SizeFor(unsigned length) { + return sizeof(ByteArray) + sizeof(uint8) * length; + } + + static inline ByteArray *cast(Object *obj) { + assert(!obj || obj->is(MapKind_ByteArray)); + return reinterpret_cast(obj); + } +}; + +} + +#endif // _include_jitcraft_fixedarray_h_ diff -r c4b1341297e5 -r ba85a47ee414 src/Functions.cpp --- a/src/Functions.cpp Mon Jun 10 00:46:29 2013 +0100 +++ b/src/Functions.cpp Mon Jun 10 01:04:43 2013 +0100 @@ -1,50 +1,50 @@ -/* - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#include "Functions.h" -#include "Modules.h" -#include "Zone.h" -#include "Heap-inl.h" - -using namespace ke; - -Function * -Function::New(Zone *zone, Handle code, Handle type, Handle name, - const SourcePosition &pos) -{ - Function *fun = Function::cast(zone->allocate(MapKind_Function, sizeof(Function), Heap::Tenure_Old)); - if (!fun) - return NULL; - - fun->code_ = code; - fun->pos_ = pos; - fun->type_ = type; - fun->name_ = name; - return fun; -} - -Native * -Native::New(Zone *zone, Handle type, size_t index) -{ - Native *fun = Native::cast(zone->allocate(MapKind_Native, sizeof(Native), Heap::Tenure_Old)); - if (!fun) - return NULL; - - fun->type_ = type; - fun->index_ = index; - return fun; -} +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#include "Functions.h" +#include "Modules.h" +#include "Zone.h" +#include "Heap-inl.h" + +using namespace ke; + +Function * +Function::New(Zone *zone, Handle code, Handle type, Handle name, + const SourcePosition &pos) +{ + Function *fun = Function::cast(zone->allocate(MapKind_Function, sizeof(Function), Heap::Tenure_Old)); + if (!fun) + return NULL; + + fun->code_ = code; + fun->pos_ = pos; + fun->type_ = type; + fun->name_ = name; + return fun; +} + +Native * +Native::New(Zone *zone, Handle type, size_t index) +{ + Native *fun = Native::cast(zone->allocate(MapKind_Native, sizeof(Native), Heap::Tenure_Old)); + if (!fun) + return NULL; + + fun->type_ = type; + fun->index_ = index; + return fun; +} diff -r c4b1341297e5 -r ba85a47ee414 src/Functions.h --- a/src/Functions.h Mon Jun 10 00:46:29 2013 +0100 +++ b/src/Functions.h Mon Jun 10 01:04:43 2013 +0100 @@ -1,113 +1,113 @@ -/* - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#ifndef _include_jitcraft_function_h_ -#define _include_jitcraft_function_h_ - -#include "Heap.h" -#include "Barriers.h" -#include "Types.h" -#include "Opcodes.h" - -namespace ke { - -class Code; -class Module; -class String; - -class Function : public Object -{ - Barriered code_; - Barriered type_; - Barriered name_; - SourcePosition pos_; - - public: - static Function *New(Zone *zone, Handle code, Handle type, Handle name, - const SourcePosition &pos); - - Code *code() const { - return code_; - } - FunctionType *type() const { - return type_; - } - String *name() const { - return name_; - } - const SourcePosition &pos() { - return pos_; - } - unsigned nargs() { - return type()->parameters()->length(); - } - Type *argType(size_t index) { - return type()->parameterAt(index); - } - - // Begin GC Descriptor. - static inline size_t offsetOfCode() { - return OFFSETOF(Function, code_); - } - static inline size_t offsetOfType() { - return OFFSETOF(Function, type_); - } - static inline size_t offsetOfName() { - return OFFSETOF(Function, name_); - } - // End GC Descriptor. - - static inline Function *cast(Object *obj) { - assert(!obj || obj->is(MapKind_Function)); - return reinterpret_cast(obj); - } -}; - -// A Native is a function with a very specialized calling convention, intended -// for earlier ISourcePawnEngine API compatibility. -class Native : public Object -{ - Barriered type_; - - // Index into the Zone's native table. - size_t index_; - - public: - static Native *New(Zone *zone, Handle type, size_t index); - - FunctionType *type() { - return type_; - } - size_t index() const { - return index_; - } - - // Begin GC Descriptor. - static inline size_t offsetOfType() { - return OFFSETOF(Native, type_); - } - // End GC Descriptor. - - static inline Native *cast(Object *obj) { - assert(!obj || obj->is(MapKind_Native)); - return reinterpret_cast(obj); - } -}; - -} - -#endif // _include_jitcraft_function_h_ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_function_h_ +#define _include_jitcraft_function_h_ + +#include "Heap.h" +#include "Barriers.h" +#include "Types.h" +#include "Opcodes.h" + +namespace ke { + +class Code; +class Module; +class String; + +class Function : public Object +{ + Barriered code_; + Barriered type_; + Barriered name_; + SourcePosition pos_; + + public: + static Function *New(Zone *zone, Handle code, Handle type, Handle name, + const SourcePosition &pos); + + Code *code() const { + return code_; + } + FunctionType *type() const { + return type_; + } + String *name() const { + return name_; + } + const SourcePosition &pos() { + return pos_; + } + unsigned nargs() { + return type()->parameters()->length(); + } + Type *argType(size_t index) { + return type()->parameterAt(index); + } + + // Begin GC Descriptor. + static inline size_t offsetOfCode() { + return OFFSETOF(Function, code_); + } + static inline size_t offsetOfType() { + return OFFSETOF(Function, type_); + } + static inline size_t offsetOfName() { + return OFFSETOF(Function, name_); + } + // End GC Descriptor. + + static inline Function *cast(Object *obj) { + assert(!obj || obj->is(MapKind_Function)); + return reinterpret_cast(obj); + } +}; + +// A Native is a function with a very specialized calling convention, intended +// for earlier ISourcePawnEngine API compatibility. +class Native : public Object +{ + Barriered type_; + + // Index into the Zone's native table. + size_t index_; + + public: + static Native *New(Zone *zone, Handle type, size_t index); + + FunctionType *type() { + return type_; + } + size_t index() const { + return index_; + } + + // Begin GC Descriptor. + static inline size_t offsetOfType() { + return OFFSETOF(Native, type_); + } + // End GC Descriptor. + + static inline Native *cast(Object *obj) { + assert(!obj || obj->is(MapKind_Native)); + return reinterpret_cast(obj); + } +}; + +} + +#endif // _include_jitcraft_function_h_ diff -r c4b1341297e5 -r ba85a47ee414 src/Interpreter.cpp --- a/src/Interpreter.cpp Mon Jun 10 00:46:29 2013 +0100 +++ b/src/Interpreter.cpp Mon Jun 10 01:04:43 2013 +0100 @@ -1,1041 +1,1041 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of JITCraft. - * - * JITCraft is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * JITCraft. If not, see http://www.gnu.org/licenses/. - */ -#include -#include "Interpreter.h" -#include "Modules.h" -#include "Zone.h" -#include "Types.h" -#include "Array.h" -#include "Reference.h" -#include "Strings.h" -#include "Environments.h" -#include "Structures.h" -#include "NativeTable.h" -#include "Packages.h" -#include "Heap-inl.h" - -using namespace ke; - -#define PUSH_I32(top, value) \ - do { \ - top.sp->i32 = value; \ - top.sp++; \ - } while (0) - -#define PUSH_F(top, value) \ - do { \ - top.sp->f = value; \ - top.sp++; \ - } while (0) - -static inline int -POP_I32(Top &top) -{ - int value = top.sp[-1].i32; - top.sp--; - return value; -} - -static inline float -POP_F(Top &top) -{ - float value = top.sp[-1].f; - top.sp--; - return value; -} - -// Pawn's original semantics specify some kind of floor operation, so -// (8 / -3 == -3) and (-8 / 3 == -1). The SourcePawn 1 compiler (spcomp) uses -// the upstream algorithm when constant folding. However the SourcePawn 1 JIT -// does not. My guess is that Borja freaked out seeing the 13-instruction x86 -// sequence necessary to get this functionality (I vaguely recall a thread -// where we discussed this with Mark Peter? But the ITB forums are gone...), -// and we just decided to break the damn thing. So SP1 was inconsistent, but -// we are not. And for completeness, we assert that the C compiler does the -// right thing, since C89 leaves it unspecified. -STATIC_ASSERT(8 / -3 == -2); -STATIC_ASSERT(-8 / 3 == -2); - -static inline bool -PushNewRef(Zone *zone, Top &top, void *location, Heap::Tenure tenure) -{ - Reference *ref = Reference::NewStack(zone, location, tenure); - if (!ref) - return false; - top.sp->ref = ref; - top.sp++; - return true; -} - -static void -GetElement(Array *array, int index, LocalSlot *vp) -{ - if (array->type()->contained()->isTraceable()) { - vp->obj = array->readObject(index); - return; - } - - switch (array->type()->contained()->pod()) { - case PrimitiveType_Float: - vp->f = array->readFloat(index); - break; - - case PrimitiveType_Char: - vp->i32 = array->readChar(index); - break; - - default: - assert(array->type()->contained()->pod() == PrimitiveType_Int32); - vp->i32 = array->readInt32(index); - break; - } -} - -static void -SetElement(Zone *zone, Array *array, int index, const LocalSlot &v) -{ - if (array->type()->contained()->isTraceable()) { - array->writeObject(zone, index, v.obj); - return; - } - - switch (array->type()->contained()->pod()) { - case PrimitiveType_Float: - array->writeFloat(index, v.f); - break; - - case PrimitiveType_Char: - array->writeChar(index, v.i32); - break; - - default: - assert(array->type()->contained()->pod() == PrimitiveType_Int32); - array->writeInt32(index, v.i32); - break; - } -} - -static void -FetchField(Struct *obj, Descriptor *desc, LocalSlot *vp) -{ - // :TODO: assert no GC. - if (desc->type()->isTraceable()) { - vp->obj = obj->get(desc); - return; - } - - switch (desc->type()->pod()) { - case PrimitiveType_Float: - vp->f = obj->get(desc); - break; - - default: - assert(desc->type()->pod() == PrimitiveType_Int32); - vp->i32 = obj->get(desc); - break; - } -} - -static void -GetField(Struct *obj, unsigned index, LocalSlot *vp) -{ - StructType *type = StructType::cast(obj->map()->type()); - Descriptor *desc = Descriptor::cast(type->fields()->at(index)); - FetchField(obj, desc, vp); -} - -static void -GetGlobal(Zone *zone, Handle obj, Handle name, LocalSlot *vp) -{ - Local type(zone, StructType::cast(obj->map()->type())); - Local desc(zone, type->lookupField(name, NULL)); - FetchField(obj, desc, vp); -} - -static void -StoreField(Zone *zone, Struct *obj, Descriptor *desc, const LocalSlot &v) -{ - // :TODO: assert no GC. - if (desc->type()->isTraceable()) { - obj->set(zone, desc, v.obj); - return; - } - - switch (desc->type()->pod()) { - case PrimitiveType_Float: - obj->set(desc, v.f); - break; - - default: - assert(desc->type()->pod() == PrimitiveType_Int32); - obj->set(desc, v.i32); - break; - } -} - -static void -SetField(Zone *zone, Struct *obj, unsigned index, const LocalSlot &v) -{ - StructType *type = StructType::cast(obj->map()->type()); - Descriptor *desc = Descriptor::cast(type->fields()->at(index)); - StoreField(zone, obj, desc, v); -} - -static void -SetGlobal(Zone *zone, Handle obj, Handle name, LocalSlot &v) -{ - Local type(zone, StructType::cast(obj->map()->type())); - Local desc(zone, type->lookupField(name, NULL)); - StoreField(zone, obj, desc, v); -} - -static bool -InvokeNative(Zone *zone, Handle native, void *identity, LocalSlot *args, unsigned argc, - int *result) -{ - NativeArgs guard; - - void *address = zone->nativeTable()->address(native->index()); - if (!address) { - Local name(zone, zone->nativeTable()->name(native->index())); - zone->reportError(Message_NativeNotBound, name->chars()); - return false; - } - - if (!zone->stack().pushNativeFrame(native, guard, argc + 1)) - return false; - - intptr_t *cells = guard.args(); - - // params[0] == argc, in ISPE - cells[0] = argc; - - // Pass the formal arguments. - Local type(zone, native->type()); - Local param(zone); - for (unsigned i = 0; i < type->parameters()->length(); i++) { - param = type->parameterAt(i); - - // For floats, we rely on the aliasing of |i32| and |f|. - LocalSlot &arg = args[-int(i) - 1]; - if (param->isInt32() || param->isFloat()) { - cells[i + 1] = arg.i32; - } else if (param->isReference()) { - cells[i + 1] = reinterpret_cast(arg.ref->location()); - } else { - assert(param->isArray()); - cells[i + 1] = reinterpret_cast(arg.array->bytes()); - } - } - - // For variadic arguments, the compiler left either arrays or references. - for (unsigned i = type->parameters()->length(); i < argc; i++) { - LocalSlot &arg = args[-int(i) - 1]; - - if (arg.obj->is(MapKind_Reference)) { - cells[i + 1] = reinterpret_cast(arg.ref->location()); - } else { - assert(arg.obj->is(MapKind_Array)); - cells[i + 1] = reinterpret_cast(arg.array->bytes()); - } - } - - typedef intptr_t (*INVOKE)(void *, intptr_t *); - INVOKE invoke = (INVOKE)address; - *result = invoke(identity, cells); - - return !zone->hasPendingException(); -} - -bool -ke::Interpret(Zone *zone, Frame *entryFrame, LocalSlot *result) -{ - Top &top = *zone->stack().top(); - if (!entryFrame) - entryFrame = top.fp; - - LocalSlot *locals = entryFrame->locals(); - - for (;;) { - Opcode op = Opcode(*top.pc); - - if (OpcodeInfo[op].nuses > 0) - assert(top.sp - OpcodeInfo[op].nuses >= top.fp->stackBase()); - - switch (op) { - -case OP_NOP: - break; - -case OP_INT: - PUSH_I32(top, READ_INT32(top.pc)); - break; - -case OP_FLOAT: - PUSH_F(top, READ_FLOAT(top.pc)); - break; - -case OP_RETURN: -case OP_RETURNVOID: -case OP_STOP: -{ - if (op == OP_RETURN) - *result = *--top.sp; - - if (top.fp == entryFrame) - goto done; - - // This must not GC. - zone->stack().popInlineFrame(); - locals = top.fp->locals(); - - if (op == OP_RETURN) - *top.sp++ = *result; - - top.pc += OpcodeInfo[*top.pc].length; - continue; -} - -case OP_POP: -{ - top.sp--; - break; -} - -case OP_JT: -{ - int value = POP_I32(top); - if (value) { - top.pc += READ_INT32(top.pc); - continue; - } - break; -} - -case OP_JF: -{ - int value = POP_I32(top); - if (!value) { - top.pc += READ_INT32(top.pc); - continue; - } - break; -} - -case OP_JUMP: -{ - top.pc += READ_INT32(top.pc); - continue; -} - -case OP_BREAK: -case OP_CONTINUE: -{ - top.fp->leaveLoop(zone); - top.pc += READ_INT32(top.pc); - continue; -} - -case OP_GETLOCAL: -{ - unsigned slot = READ_UINT32(top.pc); - assert(slot < top.fp->code()->nlocals()); - *top.sp++ = locals[slot]; - break; -} - -case OP_SETLOCAL: -{ - unsigned slot = READ_UINT32(top.pc); - assert(slot < top.fp->code()->nlocals()); - locals[slot] = top.sp[-1]; - break; -} - -case OP_ADD: -{ - top.sp[-2].i32 = top.sp[-2].i32 + top.sp[-1].i32; - top.sp--; - break; -} - -case OP_SUB: -{ - top.sp[-2].i32 = top.sp[-2].i32 - top.sp[-1].i32; - top.sp--; - break; -} - -case OP_MUL: -{ - top.sp[-2].i32 = top.sp[-2].i32 * top.sp[-1].i32; - top.sp--; - break; -} - -case OP_DIV: -{ - if (top.sp[-1].i32 == 0) { - zone->reportError(Message_IntegerDivideByZero); - goto error; - } - top.sp[-2].i32 = top.sp[-2].i32 / top.sp[-1].i32; - top.sp--; - break; -} - -case OP_MOD: -{ - if (top.sp[-1].i32 == 0) { - zone->reportError(Message_IntegerDivideByZero); - goto error; - } - top.sp[-2].i32 = top.sp[-2].i32 % top.sp[-1].i32; - top.sp--; - break; -} - -case OP_LT: -{ - top.sp[-2].i32 = top.sp[-2].i32 < top.sp[-1].i32; - top.sp--; - break; -} - -case OP_LE: -{ - top.sp[-2].i32 = top.sp[-2].i32 <= top.sp[-1].i32; - top.sp--; - break; -} - -case OP_GT: -{ - top.sp[-2].i32 = top.sp[-2].i32 > top.sp[-1].i32; - top.sp--; - break; -} - -case OP_GE: -{ - top.sp[-2].i32 = top.sp[-2].i32 >= top.sp[-1].i32; - top.sp--; - break; -} - -case OP_EQ: -{ - top.sp[-2].i32 = (top.sp[-2].i32 == top.sp[-1].i32); - top.sp--; - break; -} - -case OP_NE: -{ - top.sp[-2].i32 = (top.sp[-2].i32 != top.sp[-1].i32); - top.sp--; - break; -} - -case OP_ADD_F: -{ - top.sp[-2].f = top.sp[-2].f + top.sp[-1].f; - top.sp--; - break; -} - -case OP_SUB_F: -{ - top.sp[-2].f = top.sp[-2].f - top.sp[-1].f; - top.sp--; - break; -} - -case OP_MUL_F: -{ - top.sp[-2].f = top.sp[-2].f * top.sp[-1].f; - top.sp--; - break; -} - -case OP_DIV_F: -{ - top.sp[-2].f = top.sp[-2].f / top.sp[-1].f; - top.sp--; - break; -} - -case OP_LT_F: -{ - top.sp[-2].f = top.sp[-2].f < top.sp[-1].f; - top.sp--; - break; -} - -case OP_LE_F: -{ - top.sp[-2].f = top.sp[-2].f <= top.sp[-1].f; - top.sp--; - break; -} - -case OP_GT_F: -{ - top.sp[-2].f = top.sp[-2].f > top.sp[-1].f; - top.sp--; - break; -} - -case OP_GE_F: -{ - top.sp[-2].f = top.sp[-2].f >= top.sp[-1].f; - top.sp--; - break; -} - -case OP_EQ_F: -{ - top.sp[-2].i32 = (top.sp[-2].f == top.sp[-1].f); - top.sp--; - break; -} - -case OP_NE_F: -{ - top.sp[-2].i32 = (top.sp[-2].f != top.sp[-1].f); - top.sp--; - break; -} - -case OP_SHL: -{ - top.sp[-2].i32 = top.sp[-2].i32 << top.sp[-1].i32; - top.sp--; - break; -} - -case OP_SHR: -{ - top.sp[-2].i32 = top.sp[-2].i32 >> top.sp[-1].i32; - top.sp--; - break; -} - -case OP_USHR: -{ - top.sp[-2].i32 = unsigned(top.sp[-2].i32) >> unsigned(top.sp[-1].i32); - top.sp--; - break; -} - -case OP_BITAND: -{ - top.sp[-2].i32 = top.sp[-2].i32 & top.sp[-1].i32; - top.sp--; - break; -} - -case OP_BITOR: -{ - top.sp[-2].i32 = top.sp[-2].i32 | top.sp[-1].i32; - top.sp--; - break; -} - -case OP_BITXOR: -{ - top.sp[-2].i32 = top.sp[-2].i32 ^ top.sp[-1].i32; - top.sp--; - break; -} - -case OP_BITNOT: -{ - top.sp[-1].i32 = ~top.sp[-1].i32; - break; -} - -case OP_NOT: -{ - top.sp[-1].i32 = top.sp[-1].i32 ? 0 : 1; - break; -} - -case OP_NOT_F: -{ - top.sp[-1].i32 = top.sp[-1].f ? 0 : 1; - break; -} - -case OP_NEG: -{ - top.sp[-1].i32 = -top.sp[-1].i32; - break; -} - -case OP_NEG_F: -{ - top.sp[-1].f = -top.sp[-1].f; - break; -} - -case OP_CALL: -{ - Local newFun(zone, Function::cast(top.sp[-1].obj)); - top.sp--; - - if (!zone->stack().pushInlineFrame(newFun)) - goto error; - - locals = top.fp->locals(); - continue; -} - -case OP_GETARG: -{ - unsigned slot = READ_UINT32(top.pc); - *top.sp++ = top.fp->arg(slot); - break; -} - -case OP_SETARG: -{ - unsigned slot = READ_UINT32(top.pc); - top.fp->arg(slot) = top.sp[-1]; - break; -} - -case OP_GETELEM: -{ - Array *array = Array::cast(top.sp[-2].obj); - int index = top.sp[-1].i32; - - if (index < 0 || unsigned(index) >= array->length()) { - zone->reportError(Message_ArrayIndexOutOfBounds, index, array->length()); - goto error; - } - - GetElement(array, index, &top.sp[-2]); - top.sp--; - break; -} - -case OP_SETELEM: -{ - Array *array = Array::cast(top.sp[-3].obj); - int index = top.sp[-2].i32; - - if (index < 0 || unsigned(index) >= array->length()) { - zone->reportError(Message_ArrayIndexOutOfBounds, index, array->length()); - goto error; - } - - SetElement(zone, array, index, top.sp[-1]); - top.sp[-3] = top.sp[-1]; - top.sp -= 2; - break; -} - -case OP_CVT_I2F: -{ - int ival = POP_I32(top); - float fval = (float)ival; - PUSH_F(top, fval); - break; -} - -case OP_CVT_I2B: -{ - top.sp[-1].i32 = !!top.sp[-1].i32; - break; -} - -case OP_DUPARRAY: -{ - Local src(zone, Array::cast(top.sp[-1].obj)); - top.sp[-1].obj = Array::DeepCopy(zone, src, Heap::Tenure_Default); - if (!top.sp[-1].obj) - goto error; - break; -} - -case OP_NEWFIXED: -{ - Heap::Tenure tenure = (Heap::Tenure)top.pc[1]; - Local map(zone, ArrayMap::cast(top.fp->code()->map(READ_UINT32(top.pc + 1)))); - Array *array = Array::NewFixed(zone, map, tenure); - if (!array) - goto error; - top.sp->obj = array; - top.sp++; - break; -} - -case OP_NEWEMPTY: -{ - Heap::Tenure tenure = (Heap::Tenure)top.pc[1]; - Local map(zone, ArrayMap::cast(top.fp->code()->map(READ_UINT32(top.pc + 1)))); - Array *array = Array::NewEmpty(zone, map, tenure); - if (!array) - goto error; - top.sp->obj = array; - top.sp++; - break; -} - -case OP_NEWSIZED: -{ - Heap::Tenure tenure = (Heap::Tenure)top.pc[1]; - Local map(zone, ArrayMap::cast(top.fp->code()->map(READ_UINT32(top.pc + 1)))); - assert(map->type()->levels() < MAX_ARRAY_DEPTH); - - int dims[MAX_ARRAY_DEPTH]; - for (unsigned i = map->type()->levels() - 1; i < map->type()->levels(); i--) - dims[i] = POP_I32(top); - - Array *array = Array::NewSized(zone, map, dims, tenure); - if (!array) - goto error; - top.sp->obj = array; - top.sp++; - break; -} - -case OP_NEWSTRUCT: -{ - Heap::Tenure tenure = (Heap::Tenure)top.pc[1]; - Local map(zone, StructMap::cast(top.fp->code()->map(READ_UINT32(top.pc + 1)))); - top.sp->obj = Struct::New(zone, map, tenure); - if (!top.sp->obj) - goto error; - top.sp++; - break; -} - -case OP_SAVELIFO: -{ - unsigned slot = READ_UINT32(top.pc); - locals[slot].address = zone->heap().lifoPosition(); - break; -} - -case OP_UNWINDLIFO: -{ - unsigned slot = READ_UINT32(top.pc); - zone->heap().unwindLifo(locals[slot].address); - break; -} - -case OP_VARREF_LIFO: -{ - unsigned slot = READ_UINT32(top.pc); - if (!PushNewRef(zone, top, &locals[slot], Heap::Tenure_Stack)) - goto error; - break; -} - -case OP_ARGREF_LIFO: -{ - unsigned slot = READ_UINT32(top.pc); - if (!PushNewRef(zone, top, &top.fp->arg(slot), Heap::Tenure_Stack)) - goto error; - break; -} - -case OP_SLOTREF_LIFO: -{ - Reference *ref = Reference::NewStack(zone, top.sp[-1], Heap::Tenure_Stack); - if (!ref) - goto error; - top.sp[-1].ref = ref; - break; -} - -case OP_GETREF_I: -{ - top.sp[-1].i32 = top.sp[-1].ref->getInt(); - break; -} - -case OP_SETREF_I: -{ - top.sp[-2].ref->setInt(top.sp[-1].i32); - top.sp[-2].i32 = top.sp[-1].i32; - top.sp--; - break; -} - -case OP_GETREF_F: -{ - top.sp[-1].f = top.sp[-1].ref->getFloat(); - break; -} - -case OP_SETREF_F: -{ - top.sp[-2].ref->setFloat(top.sp[-1].f); - top.sp[-2].f = top.sp[-1].f; - break; -} - -case OP_OBJECT: -{ - top.sp->obj = top.fp->code()->object(READ_UINT32(top.pc)); - top.sp++; - break; -} - -case OP_COPYARRAY: -{ - // Note for this assignment opcode, we return the left-hand side, not the - // right, since in this case the result of the expression could be different. - Local left(zone, Array::cast(top.sp[-2].array)); - Local right(zone, Array::cast(top.sp[-1].array)); - if (!Array::ShallowCopy(zone, left, right)) - goto error; - top.sp--; - break; -} - -case OP_DUP: -{ - top.sp++; - top.sp[-1] = top.sp[-2]; - break; -} - -case OP_DUP2: -{ - top.sp += 2; - top.sp[-2] = top.sp[-4]; - top.sp[-1] = top.sp[-3]; - break; -} - -case OP_SWAP: -{ - LocalSlot slot = top.sp[-2]; - top.sp[-2] = top.sp[-1]; - top.sp[-1] = slot; - break; -} - -case OP_ROLL3: -{ - LocalSlot slot = top.sp[-3]; - top.sp[-3] = top.sp[-2]; - top.sp[-2] = top.sp[-1]; - top.sp[-1] = slot; - break; -} - -case OP_PICK2: -{ - top.sp++; - top.sp[-1] = top.sp[-3]; - break; -} - -case OP_PICK3: -{ - top.sp++; - top.sp[-1] = top.sp[-4]; - break; -} - -case OP_CALLNATIVE: -{ - unsigned argc = READ_UINT32(top.pc); - - Local native(zone, Native::cast(top.sp[-1].obj)); - top.sp--; - - int result; - void *nativeIdentity = top.fp->code()->module()->parent()->getNativeIdentity(); - if (!InvokeNative(zone, native, nativeIdentity, top.sp, argc, &result)) - goto error; - - top.sp -= argc; - PUSH_I32(top, result); - break; -} - -case OP_BITCAST: -{ - // Because the stack is a union, this is a no-op. - break; -} - -case OP_TRUE: -{ - top.sp++; - top.sp[-1].i32 = 1; - break; -} - -case OP_FALSE: -{ - top.sp++; - top.sp[-1].i32 = 0; - break; -} - -case OP_GETFIELD: -{ - Struct *obj; - if (top.sp[-1].obj->is(MapKind_Module)) - obj = Module::cast(top.sp[-1].obj)->globals(); - else - obj = Struct::cast(top.sp[-1].obj); - GetField(obj, READ_UINT32(top.pc), &top.sp[-1]); - break; -} - -case OP_SETFIELD: -{ - Struct *obj = Struct::cast(top.sp[-2].obj); - SetField(zone, obj, READ_UINT32(top.pc), top.sp[-1]); - top.sp[-2] = top.sp[-1]; - top.sp--; - break; -} - -case OP_COPYSTRUCT: -{ - Local left(zone, Struct::cast(top.sp[-2].obj)); - Local right(zone, Struct::cast(top.sp[-1].obj)); - if (!Struct::Copy(zone, left, right)) - goto error; - top.sp[-2].obj = top.sp[-1].obj; - top.sp--; - break; -} - -case OP_TABLESWITCH: -{ - int value = top.sp[-1].i32; - int low = READ_TABLESWITCH_LOW(top.pc); - int high = READ_TABLESWITCH_HIGH(top.pc); - - top.sp--; - - unsigned index = unsigned(value - low); - unsigned tablesize = TableSwitchEntries(low, high); - if (index < tablesize) { - top.pc += OP_TABLESWITCH_LENGTH + OP_JUMP_LENGTH; - top.pc += index * OP_JUMP_LENGTH; - continue; - } - break; -} - -case OP_SIZEOF: -{ - Array *array = Array::cast(top.sp[-1].obj); - top.sp[-1].i32 = array->length(); - break; -} - -case OP_PUBLIC: -{ - Local fun(zone, Function::cast(top.sp[-1].obj)); - zone->registerPublic(fun); - top.sp--; - break; -} - -case OP_SETGLOBAL: -{ - Local code(zone, top.fp->code()); - Local obj(zone, code->module()->globals()); - Local name(zone, String::cast(code->strings()->at(READ_UINT32(top.pc)))); - SetGlobal(zone, obj, name, top.sp[-1]); - break; -} - -case OP_GETGLOBAL: -{ - Local code(zone, top.fp->code()); - Local obj(zone, code->module()->globals()); - Local name(zone, String::cast(code->strings()->at(READ_UINT32(top.pc)))); - GetGlobal(zone, obj, name, top.sp); - top.sp++; - break; -} - -case OP_ENTERLOOP: -{ - top.fp->enterLoop(zone); - break; -} - -case OP_LEAVELOOP: -{ - top.fp->leaveLoop(zone); - break; -} - -case OP_DEPARRAY: -{ - Local map(zone, ArrayMap::cast(top.fp->code()->object(READ_UINT32(top.pc)))); - Local array(zone, Array::cast(top.sp[-2].obj)); - int index = top.sp[-1].i32; - - // We allow taking 0-length dependent arrays. - if (index < 0 || unsigned(index) > array->length()) { - zone->reportError(Message_ArrayIndexOutOfBounds, index, array->length()); - goto error; - } - - top.sp[-2].obj = DependentArray::New(zone, array, map, index); - if (!top.sp[-2].obj) - goto error; - - top.sp--; - break; -} - -case OP_IMPORT: -{ - unsigned index = READ_UINT32(top.pc); - - top.sp++; - top.sp[-1].obj = top.fp->code()->module()->getImportAt(index); - break; -} - -default: - assert(false); - return false; - - } - - top.pc += OpcodeInfo[op].length; - assert(top.sp >= top.fp->stackBase()); - } - -done: - return true; - -error: - // Unwind frames until we can continue. - while (top.fp != entryFrame) - zone->stack().popInlineFrame(); - return false; -} +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include +#include "Interpreter.h" +#include "Modules.h" +#include "Zone.h" +#include "Types.h" +#include "Array.h" +#include "Reference.h" +#include "Strings.h" +#include "Environments.h" +#include "Structures.h" +#include "NativeTable.h" +#include "Packages.h" +#include "Heap-inl.h" + +using namespace ke; + +#define PUSH_I32(top, value) \ + do { \ + top.sp->i32 = value; \ + top.sp++; \ + } while (0) + +#define PUSH_F(top, value) \ + do { \ + top.sp->f = value; \ + top.sp++; \ + } while (0) + +static inline int +POP_I32(Top &top) +{ + int value = top.sp[-1].i32; + top.sp--; + return value; +} + +static inline float +POP_F(Top &top) +{ + float value = top.sp[-1].f; + top.sp--; + return value; +} + +// Pawn's original semantics specify some kind of floor operation, so +// (8 / -3 == -3) and (-8 / 3 == -1). The SourcePawn 1 compiler (spcomp) uses +// the upstream algorithm when constant folding. However the SourcePawn 1 JIT +// does not. My guess is that Borja freaked out seeing the 13-instruction x86 +// sequence necessary to get this functionality (I vaguely recall a thread +// where we discussed this with Mark Peter? But the ITB forums are gone...), +// and we just decided to break the damn thing. So SP1 was inconsistent, but +// we are not. And for completeness, we assert that the C compiler does the +// right thing, since C89 leaves it unspecified. +STATIC_ASSERT(8 / -3 == -2); +STATIC_ASSERT(-8 / 3 == -2); + +static inline bool +PushNewRef(Zone *zone, Top &top, void *location, Heap::Tenure tenure) +{ + Reference *ref = Reference::NewStack(zone, location, tenure); + if (!ref) + return false; + top.sp->ref = ref; + top.sp++; + return true; +} + +static void +GetElement(Array *array, int index, LocalSlot *vp) +{ + if (array->type()->contained()->isTraceable()) { + vp->obj = array->readObject(index); + return; + } + + switch (array->type()->contained()->pod()) { + case PrimitiveType_Float: + vp->f = array->readFloat(index); + break; + + case PrimitiveType_Char: + vp->i32 = array->readChar(index); + break; + + default: + assert(array->type()->contained()->pod() == PrimitiveType_Int32); + vp->i32 = array->readInt32(index); + break; + } +} + +static void +SetElement(Zone *zone, Array *array, int index, const LocalSlot &v) +{ + if (array->type()->contained()->isTraceable()) { + array->writeObject(zone, index, v.obj); + return; + } + + switch (array->type()->contained()->pod()) { + case PrimitiveType_Float: + array->writeFloat(index, v.f); + break; + + case PrimitiveType_Char: + array->writeChar(index, v.i32); + break; + + default: + assert(array->type()->contained()->pod() == PrimitiveType_Int32); + array->writeInt32(index, v.i32); + break; + } +} + +static void +FetchField(Struct *obj, Descriptor *desc, LocalSlot *vp) +{ + // :TODO: assert no GC. + if (desc->type()->isTraceable()) { + vp->obj = obj->get(desc); + return; + } + + switch (desc->type()->pod()) { + case PrimitiveType_Float: + vp->f = obj->get(desc); + break; + + default: + assert(desc->type()->pod() == PrimitiveType_Int32); + vp->i32 = obj->get(desc); + break; + } +} + +static void +GetField(Struct *obj, unsigned index, LocalSlot *vp) +{ + StructType *type = StructType::cast(obj->map()->type()); + Descriptor *desc = Descriptor::cast(type->fields()->at(index)); + FetchField(obj, desc, vp); +} + +static void +GetGlobal(Zone *zone, Handle obj, Handle name, LocalSlot *vp) +{ + Local type(zone, StructType::cast(obj->map()->type())); + Local desc(zone, type->lookupField(name, NULL)); + FetchField(obj, desc, vp); +} + +static void +StoreField(Zone *zone, Struct *obj, Descriptor *desc, const LocalSlot &v) +{ + // :TODO: assert no GC. + if (desc->type()->isTraceable()) { + obj->set(zone, desc, v.obj); + return; + } + + switch (desc->type()->pod()) { + case PrimitiveType_Float: + obj->set(desc, v.f); + break; + + default: + assert(desc->type()->pod() == PrimitiveType_Int32); + obj->set(desc, v.i32); + break; + } +} + +static void +SetField(Zone *zone, Struct *obj, unsigned index, const LocalSlot &v) +{ + StructType *type = StructType::cast(obj->map()->type()); + Descriptor *desc = Descriptor::cast(type->fields()->at(index)); + StoreField(zone, obj, desc, v); +} + +static void +SetGlobal(Zone *zone, Handle obj, Handle name, LocalSlot &v) +{ + Local type(zone, StructType::cast(obj->map()->type())); + Local desc(zone, type->lookupField(name, NULL)); + StoreField(zone, obj, desc, v); +} + +static bool +InvokeNative(Zone *zone, Handle native, void *identity, LocalSlot *args, unsigned argc, + int *result) +{ + NativeArgs guard; + + void *address = zone->nativeTable()->address(native->index()); + if (!address) { + Local name(zone, zone->nativeTable()->name(native->index())); + zone->reportError(Message_NativeNotBound, name->chars()); + return false; + } + + if (!zone->stack().pushNativeFrame(native, guard, argc + 1)) + return false; + + intptr_t *cells = guard.args(); + + // params[0] == argc, in ISPE + cells[0] = argc; + + // Pass the formal arguments. + Local type(zone, native->type()); + Local param(zone); + for (unsigned i = 0; i < type->parameters()->length(); i++) { + param = type->parameterAt(i); + + // For floats, we rely on the aliasing of |i32| and |f|. + LocalSlot &arg = args[-int(i) - 1]; + if (param->isInt32() || param->isFloat()) { + cells[i + 1] = arg.i32; + } else if (param->isReference()) { + cells[i + 1] = reinterpret_cast(arg.ref->location()); + } else { + assert(param->isArray()); + cells[i + 1] = reinterpret_cast(arg.array->bytes()); + } + } + + // For variadic arguments, the compiler left either arrays or references. + for (unsigned i = type->parameters()->length(); i < argc; i++) { + LocalSlot &arg = args[-int(i) - 1]; + + if (arg.obj->is(MapKind_Reference)) { + cells[i + 1] = reinterpret_cast(arg.ref->location()); + } else { + assert(arg.obj->is(MapKind_Array)); + cells[i + 1] = reinterpret_cast(arg.array->bytes()); + } + } + + typedef intptr_t (*INVOKE)(void *, intptr_t *); + INVOKE invoke = (INVOKE)address; + *result = invoke(identity, cells); + + return !zone->hasPendingException(); +} + +bool +ke::Interpret(Zone *zone, Frame *entryFrame, LocalSlot *result) +{ + Top &top = *zone->stack().top(); + if (!entryFrame) + entryFrame = top.fp; + + LocalSlot *locals = entryFrame->locals(); + + for (;;) { + Opcode op = Opcode(*top.pc); + + if (OpcodeInfo[op].nuses > 0) + assert(top.sp - OpcodeInfo[op].nuses >= top.fp->stackBase()); + + switch (op) { + +case OP_NOP: + break; + +case OP_INT: + PUSH_I32(top, READ_INT32(top.pc)); + break; + +case OP_FLOAT: + PUSH_F(top, READ_FLOAT(top.pc)); + break; + +case OP_RETURN: +case OP_RETURNVOID: +case OP_STOP: +{ + if (op == OP_RETURN) + *result = *--top.sp; + + if (top.fp == entryFrame) + goto done; + + // This must not GC. + zone->stack().popInlineFrame(); + locals = top.fp->locals(); + + if (op == OP_RETURN) + *top.sp++ = *result; + + top.pc += OpcodeInfo[*top.pc].length; + continue; +} + +case OP_POP: +{ + top.sp--; + break; +} + +case OP_JT: +{ + int value = POP_I32(top); + if (value) { + top.pc += READ_INT32(top.pc); + continue; + } + break; +} + +case OP_JF: +{ + int value = POP_I32(top); + if (!value) { + top.pc += READ_INT32(top.pc); + continue; + } + break; +} + +case OP_JUMP: +{ + top.pc += READ_INT32(top.pc); + continue; +} + +case OP_BREAK: +case OP_CONTINUE: +{ + top.fp->leaveLoop(zone); + top.pc += READ_INT32(top.pc); + continue; +} + +case OP_GETLOCAL: +{ + unsigned slot = READ_UINT32(top.pc); + assert(slot < top.fp->code()->nlocals()); + *top.sp++ = locals[slot]; + break; +} + +case OP_SETLOCAL: +{ + unsigned slot = READ_UINT32(top.pc); + assert(slot < top.fp->code()->nlocals()); + locals[slot] = top.sp[-1]; + break; +} + +case OP_ADD: +{ + top.sp[-2].i32 = top.sp[-2].i32 + top.sp[-1].i32; + top.sp--; + break; +} + +case OP_SUB: +{ + top.sp[-2].i32 = top.sp[-2].i32 - top.sp[-1].i32; + top.sp--; + break; +} + +case OP_MUL: +{ + top.sp[-2].i32 = top.sp[-2].i32 * top.sp[-1].i32; + top.sp--; + break; +} + +case OP_DIV: +{ + if (top.sp[-1].i32 == 0) { + zone->reportError(Message_IntegerDivideByZero); + goto error; + } + top.sp[-2].i32 = top.sp[-2].i32 / top.sp[-1].i32; + top.sp--; + break; +} + +case OP_MOD: +{ + if (top.sp[-1].i32 == 0) { + zone->reportError(Message_IntegerDivideByZero); + goto error; + } + top.sp[-2].i32 = top.sp[-2].i32 % top.sp[-1].i32; + top.sp--; + break; +} + +case OP_LT: +{ + top.sp[-2].i32 = top.sp[-2].i32 < top.sp[-1].i32; + top.sp--; + break; +} + +case OP_LE: +{ + top.sp[-2].i32 = top.sp[-2].i32 <= top.sp[-1].i32; + top.sp--; + break; +} + +case OP_GT: +{ + top.sp[-2].i32 = top.sp[-2].i32 > top.sp[-1].i32; + top.sp--; + break; +} + +case OP_GE: +{ + top.sp[-2].i32 = top.sp[-2].i32 >= top.sp[-1].i32; + top.sp--; + break; +} + +case OP_EQ: +{ + top.sp[-2].i32 = (top.sp[-2].i32 == top.sp[-1].i32); + top.sp--; + break; +} + +case OP_NE: +{ + top.sp[-2].i32 = (top.sp[-2].i32 != top.sp[-1].i32); + top.sp--; + break; +} + +case OP_ADD_F: +{ + top.sp[-2].f = top.sp[-2].f + top.sp[-1].f; + top.sp--; + break; +} + +case OP_SUB_F: +{ + top.sp[-2].f = top.sp[-2].f - top.sp[-1].f; + top.sp--; + break; +} + +case OP_MUL_F: +{ + top.sp[-2].f = top.sp[-2].f * top.sp[-1].f; + top.sp--; + break; +} + +case OP_DIV_F: +{ + top.sp[-2].f = top.sp[-2].f / top.sp[-1].f; + top.sp--; + break; +} + +case OP_LT_F: +{ + top.sp[-2].f = top.sp[-2].f < top.sp[-1].f; + top.sp--; + break; +} + +case OP_LE_F: +{ + top.sp[-2].f = top.sp[-2].f <= top.sp[-1].f; + top.sp--; + break; +} + +case OP_GT_F: +{ + top.sp[-2].f = top.sp[-2].f > top.sp[-1].f; + top.sp--; + break; +} + +case OP_GE_F: +{ + top.sp[-2].f = top.sp[-2].f >= top.sp[-1].f; + top.sp--; + break; +} + +case OP_EQ_F: +{ + top.sp[-2].i32 = (top.sp[-2].f == top.sp[-1].f); + top.sp--; + break; +} + +case OP_NE_F: +{ + top.sp[-2].i32 = (top.sp[-2].f != top.sp[-1].f); + top.sp--; + break; +} + +case OP_SHL: +{ + top.sp[-2].i32 = top.sp[-2].i32 << top.sp[-1].i32; + top.sp--; + break; +} + +case OP_SHR: +{ + top.sp[-2].i32 = top.sp[-2].i32 >> top.sp[-1].i32; + top.sp--; + break; +} + +case OP_USHR: +{ + top.sp[-2].i32 = unsigned(top.sp[-2].i32) >> unsigned(top.sp[-1].i32); + top.sp--; + break; +} + +case OP_BITAND: +{ + top.sp[-2].i32 = top.sp[-2].i32 & top.sp[-1].i32; + top.sp--; + break; +} + +case OP_BITOR: +{ + top.sp[-2].i32 = top.sp[-2].i32 | top.sp[-1].i32; + top.sp--; + break; +} + +case OP_BITXOR: +{ + top.sp[-2].i32 = top.sp[-2].i32 ^ top.sp[-1].i32; + top.sp--; + break; +} + +case OP_BITNOT: +{ + top.sp[-1].i32 = ~top.sp[-1].i32; + break; +} + +case OP_NOT: +{ + top.sp[-1].i32 = top.sp[-1].i32 ? 0 : 1; + break; +} + +case OP_NOT_F: +{ + top.sp[-1].i32 = top.sp[-1].f ? 0 : 1; + break; +} + +case OP_NEG: +{ + top.sp[-1].i32 = -top.sp[-1].i32; + break; +} + +case OP_NEG_F: +{ + top.sp[-1].f = -top.sp[-1].f; + break; +} + +case OP_CALL: +{ + Local newFun(zone, Function::cast(top.sp[-1].obj)); + top.sp--; + + if (!zone->stack().pushInlineFrame(newFun)) + goto error; + + locals = top.fp->locals(); + continue; +} + +case OP_GETARG: +{ + unsigned slot = READ_UINT32(top.pc); + *top.sp++ = top.fp->arg(slot); + break; +} + +case OP_SETARG: +{ + unsigned slot = READ_UINT32(top.pc); + top.fp->arg(slot) = top.sp[-1]; + break; +} + +case OP_GETELEM: +{ + Array *array = Array::cast(top.sp[-2].obj); + int index = top.sp[-1].i32; + + if (index < 0 || unsigned(index) >= array->length()) { + zone->reportError(Message_ArrayIndexOutOfBounds, index, array->length()); + goto error; + } + + GetElement(array, index, &top.sp[-2]); + top.sp--; + break; +} + +case OP_SETELEM: +{ + Array *array = Array::cast(top.sp[-3].obj); + int index = top.sp[-2].i32; + + if (index < 0 || unsigned(index) >= array->length()) { + zone->reportError(Message_ArrayIndexOutOfBounds, index, array->length()); + goto error; + } + + SetElement(zone, array, index, top.sp[-1]); + top.sp[-3] = top.sp[-1]; + top.sp -= 2; + break; +} + +case OP_CVT_I2F: +{ + int ival = POP_I32(top); + float fval = (float)ival; + PUSH_F(top, fval); + break; +} + +case OP_CVT_I2B: +{ + top.sp[-1].i32 = !!top.sp[-1].i32; + break; +} + +case OP_DUPARRAY: +{ + Local src(zone, Array::cast(top.sp[-1].obj)); + top.sp[-1].obj = Array::DeepCopy(zone, src, Heap::Tenure_Default); + if (!top.sp[-1].obj) + goto error; + break; +} + +case OP_NEWFIXED: +{ + Heap::Tenure tenure = (Heap::Tenure)top.pc[1]; + Local map(zone, ArrayMap::cast(top.fp->code()->map(READ_UINT32(top.pc + 1)))); + Array *array = Array::NewFixed(zone, map, tenure); + if (!array) + goto error; + top.sp->obj = array; + top.sp++; + break; +} + +case OP_NEWEMPTY: +{ + Heap::Tenure tenure = (Heap::Tenure)top.pc[1]; + Local map(zone, ArrayMap::cast(top.fp->code()->map(READ_UINT32(top.pc + 1)))); + Array *array = Array::NewEmpty(zone, map, tenure); + if (!array) + goto error; + top.sp->obj = array; + top.sp++; + break; +} + +case OP_NEWSIZED: +{ + Heap::Tenure tenure = (Heap::Tenure)top.pc[1]; + Local map(zone, ArrayMap::cast(top.fp->code()->map(READ_UINT32(top.pc + 1)))); + assert(map->type()->levels() < MAX_ARRAY_DEPTH); + + int dims[MAX_ARRAY_DEPTH]; + for (unsigned i = map->type()->levels() - 1; i < map->type()->levels(); i--) + dims[i] = POP_I32(top); + + Array *array = Array::NewSized(zone, map, dims, tenure); + if (!array) + goto error; + top.sp->obj = array; + top.sp++; + break; +} + +case OP_NEWSTRUCT: +{ + Heap::Tenure tenure = (Heap::Tenure)top.pc[1]; + Local map(zone, StructMap::cast(top.fp->code()->map(READ_UINT32(top.pc + 1)))); + top.sp->obj = Struct::New(zone, map, tenure); + if (!top.sp->obj) + goto error; + top.sp++; + break; +} + +case OP_SAVELIFO: +{ + unsigned slot = READ_UINT32(top.pc); + locals[slot].address = zone->heap().lifoPosition(); + break; +} + +case OP_UNWINDLIFO: +{ + unsigned slot = READ_UINT32(top.pc); + zone->heap().unwindLifo(locals[slot].address); + break; +} + +case OP_VARREF_LIFO: +{ + unsigned slot = READ_UINT32(top.pc); + if (!PushNewRef(zone, top, &locals[slot], Heap::Tenure_Stack)) + goto error; + break; +} + +case OP_ARGREF_LIFO: +{ + unsigned slot = READ_UINT32(top.pc); + if (!PushNewRef(zone, top, &top.fp->arg(slot), Heap::Tenure_Stack)) + goto error; + break; +} + +case OP_SLOTREF_LIFO: +{ + Reference *ref = Reference::NewStack(zone, top.sp[-1], Heap::Tenure_Stack); + if (!ref) + goto error; + top.sp[-1].ref = ref; + break; +} + +case OP_GETREF_I: +{ + top.sp[-1].i32 = top.sp[-1].ref->getInt(); + break; +} + +case OP_SETREF_I: +{ + top.sp[-2].ref->setInt(top.sp[-1].i32); + top.sp[-2].i32 = top.sp[-1].i32; + top.sp--; + break; +} + +case OP_GETREF_F: +{ + top.sp[-1].f = top.sp[-1].ref->getFloat(); + break; +} + +case OP_SETREF_F: +{ + top.sp[-2].ref->setFloat(top.sp[-1].f); + top.sp[-2].f = top.sp[-1].f; + break; +} + +case OP_OBJECT: +{ + top.sp->obj = top.fp->code()->object(READ_UINT32(top.pc)); + top.sp++; + break; +} + +case OP_COPYARRAY: +{ + // Note for this assignment opcode, we return the left-hand side, not the + // right, since in this case the result of the expression could be different. + Local left(zone, Array::cast(top.sp[-2].array)); + Local right(zone, Array::cast(top.sp[-1].array)); + if (!Array::ShallowCopy(zone, left, right)) + goto error; + top.sp--; + break; +} + +case OP_DUP: +{ + top.sp++; + top.sp[-1] = top.sp[-2]; + break; +} + +case OP_DUP2: +{ + top.sp += 2; + top.sp[-2] = top.sp[-4]; + top.sp[-1] = top.sp[-3]; + break; +} + +case OP_SWAP: +{ + LocalSlot slot = top.sp[-2]; + top.sp[-2] = top.sp[-1]; + top.sp[-1] = slot; + break; +} + +case OP_ROLL3: +{ + LocalSlot slot = top.sp[-3]; + top.sp[-3] = top.sp[-2]; + top.sp[-2] = top.sp[-1]; + top.sp[-1] = slot; + break; +} + +case OP_PICK2: +{ + top.sp++; + top.sp[-1] = top.sp[-3]; + break; +} + +case OP_PICK3: +{ + top.sp++; + top.sp[-1] = top.sp[-4]; + break; +} + +case OP_CALLNATIVE: +{ + unsigned argc = READ_UINT32(top.pc); + + Local native(zone, Native::cast(top.sp[-1].obj)); + top.sp--; + + int result; + void *nativeIdentity = top.fp->code()->module()->parent()->getNativeIdentity(); + if (!InvokeNative(zone, native, nativeIdentity, top.sp, argc, &result)) + goto error; + + top.sp -= argc; + PUSH_I32(top, result); + break; +} + +case OP_BITCAST: +{ + // Because the stack is a union, this is a no-op. + break; +} + +case OP_TRUE: +{ + top.sp++; + top.sp[-1].i32 = 1; + break; +} + +case OP_FALSE: +{ + top.sp++; + top.sp[-1].i32 = 0; + break; +} + +case OP_GETFIELD: +{ + Struct *obj; + if (top.sp[-1].obj->is(MapKind_Module)) + obj = Module::cast(top.sp[-1].obj)->globals(); + else + obj = Struct::cast(top.sp[-1].obj); + GetField(obj, READ_UINT32(top.pc), &top.sp[-1]); + break; +} + +case OP_SETFIELD: +{ + Struct *obj = Struct::cast(top.sp[-2].obj); + SetField(zone, obj, READ_UINT32(top.pc), top.sp[-1]); + top.sp[-2] = top.sp[-1]; + top.sp--; + break; +} + +case OP_COPYSTRUCT: +{ + Local left(zone, Struct::cast(top.sp[-2].obj)); + Local right(zone, Struct::cast(top.sp[-1].obj)); + if (!Struct::Copy(zone, left, right)) + goto error; + top.sp[-2].obj = top.sp[-1].obj; + top.sp--; + break; +} + +case OP_TABLESWITCH: +{ + int value = top.sp[-1].i32; + int low = READ_TABLESWITCH_LOW(top.pc); + int high = READ_TABLESWITCH_HIGH(top.pc); + + top.sp--; + + unsigned index = unsigned(value - low); + unsigned tablesize = TableSwitchEntries(low, high); + if (index < tablesize) { + top.pc += OP_TABLESWITCH_LENGTH + OP_JUMP_LENGTH; + top.pc += index * OP_JUMP_LENGTH; + continue; + } + break; +} + +case OP_SIZEOF: +{ + Array *array = Array::cast(top.sp[-1].obj); + top.sp[-1].i32 = array->length(); + break; +} + +case OP_PUBLIC: +{ + Local fun(zone, Function::cast(top.sp[-1].obj)); + zone->registerPublic(fun); + top.sp--; + break; +} + +case OP_SETGLOBAL: +{ + Local code(zone, top.fp->code()); + Local obj(zone, code->module()->globals()); + Local name(zone, String::cast(code->strings()->at(READ_UINT32(top.pc)))); + SetGlobal(zone, obj, name, top.sp[-1]); + break; +} + +case OP_GETGLOBAL: +{ + Local code(zone, top.fp->code()); + Local obj(zone, code->module()->globals()); + Local name(zone, String::cast(code->strings()->at(READ_UINT32(top.pc)))); + GetGlobal(zone, obj, name, top.sp); + top.sp++; + break; +} + +case OP_ENTERLOOP: +{ + top.fp->enterLoop(zone); + break; +} + +case OP_LEAVELOOP: +{ + top.fp->leaveLoop(zone); + break; +} + +case OP_DEPARRAY: +{ + Local map(zone, ArrayMap::cast(top.fp->code()->object(READ_UINT32(top.pc)))); + Local array(zone, Array::cast(top.sp[-2].obj)); + int index = top.sp[-1].i32; + + // We allow taking 0-length dependent arrays. + if (index < 0 || unsigned(index) > array->length()) { + zone->reportError(Message_ArrayIndexOutOfBounds, index, array->length()); + goto error; + } + + top.sp[-2].obj = DependentArray::New(zone, array, map, index); + if (!top.sp[-2].obj) + goto error; + + top.sp--; + break; +} + +case OP_IMPORT: +{ + unsigned index = READ_UINT32(top.pc); + + top.sp++; + top.sp[-1].obj = top.fp->code()->module()->getImportAt(index); + break; +} + +default: + assert(false); + return false; + + } + + top.pc += OpcodeInfo[op].length; + assert(top.sp >= top.fp->stackBase()); + } + +done: + return true; + +error: + // Unwind frames until we can continue. + while (top.fp != entryFrame) + zone->stack().popInlineFrame(); + return false; +} diff -r c4b1341297e5 -r ba85a47ee414 src/Interpreter.h --- a/src/Interpreter.h Mon Jun 10 00:46:29 2013 +0100 +++ b/src/Interpreter.h Mon Jun 10 01:04:43 2013 +0100 @@ -1,29 +1,29 @@ -/* - * Copyright (C) 2012 David Anderson - * - * This file is part of JITCraft. - * - * JITCraft is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * JITCraft. If not, see http://www.gnu.org/licenses/. - */ -#ifndef _include_jc_interpreter_h_ -#define _include_jc_interpreter_h_ - -#include "Stack.h" - -namespace ke { - -bool Interpret(Zone *zone, Frame *frame, LocalSlot *result); - -} - -#endif // _include_jc_interpreter_h_ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jc_interpreter_h_ +#define _include_jc_interpreter_h_ + +#include "Stack.h" + +namespace ke { + +bool Interpret(Zone *zone, Frame *frame, LocalSlot *result); + +} + +#endif // _include_jc_interpreter_h_ diff -r c4b1341297e5 -r ba85a47ee414 src/Messages.h --- a/src/Messages.h Mon Jun 10 00:46:29 2013 +0100 +++ b/src/Messages.h Mon Jun 10 01:04:43 2013 +0100 @@ -1,48 +1,48 @@ -/* - * Copyright (C) 2012 David Anderson - * - * This file is part of JITCraft. - * - * JITCraft is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * JITCraft. If not, see http://www.gnu.org/licenses/. - */ -#ifndef _include_jitcraft_messages_h_ -#define _include_jitcraft_messages_h_ - -namespace ke { - -enum MessageType -{ - MessageType_SyntaxError, - MessageType_TypeError, - MessageType_SystemError -}; - -enum Message -{ -#define MSG(Name, Type, String) Message_##Name, -# include "Messages.tbl" -#undef MSG - MessageType_Total -}; - -struct MessageInfo -{ - MessageType type; - const char *format; -}; - -extern const MessageInfo Messages[]; - -} - -#endif // _include_jitcraft_messages_h_ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_messages_h_ +#define _include_jitcraft_messages_h_ + +namespace ke { + +enum MessageType +{ + MessageType_SyntaxError, + MessageType_TypeError, + MessageType_SystemError +}; + +enum Message +{ +#define MSG(Name, Type, String) Message_##Name, +# include "Messages.tbl" +#undef MSG + MessageType_Total +}; + +struct MessageInfo +{ + MessageType type; + const char *format; +}; + +extern const MessageInfo Messages[]; + +} + +#endif // _include_jitcraft_messages_h_ diff -r c4b1341297e5 -r ba85a47ee414 src/Modules.cpp --- a/src/Modules.cpp Mon Jun 10 00:46:29 2013 +0100 +++ b/src/Modules.cpp Mon Jun 10 01:04:43 2013 +0100 @@ -1,40 +1,40 @@ -/* - * Copyright (C) 2012 David Anderson - * - * This file is part of JITCraft. - * - * JITCraft is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * JITCraft. If not, see http://www.gnu.org/licenses/. - */ -#include "Modules.h" -#include "Zone.h" -#include "Heap.h" -#include "Heap-inl.h" - -using namespace ke; - -Module * -Module::New(Zone *zone, Handle package, Handle path, Handle name) -{ - Local module(zone, Module::cast(zone->allocate(MapKind_Module, sizeof(Module), Heap::Tenure_Old))); - if (!module) - return NULL; - - module->parent_ = package; - module->path_ = path; - module->name_ = name; - module->globals_ = NULL; - module->nativeIdentity_ = NULL; - module->globals_ = NULL; - module->unit_ = NULL; - return module; -} +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include "Modules.h" +#include "Zone.h" +#include "Heap.h" +#include "Heap-inl.h" + +using namespace ke; + +Module * +Module::New(Zone *zone, Handle package, Handle path, Handle name) +{ + Local module(zone, Module::cast(zone->allocate(MapKind_Module, sizeof(Module), Heap::Tenure_Old))); + if (!module) + return NULL; + + module->parent_ = package; + module->path_ = path; + module->name_ = name; + module->globals_ = NULL; + module->nativeIdentity_ = NULL; + module->globals_ = NULL; + module->unit_ = NULL; + return module; +} diff -r c4b1341297e5 -r ba85a47ee414 src/Modules.h --- a/src/Modules.h Mon Jun 10 00:46:29 2013 +0100 +++ b/src/Modules.h Mon Jun 10 01:04:43 2013 +0100 @@ -1,107 +1,107 @@ -/* - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#ifndef _include_jc_module_h_ -#define _include_jc_module_h_ - -#include "Heap.h" -#include "Code.h" -#include "Functions.h" -#include "FixedArray.h" -#include "Packages.h" - -namespace ke { - -class Environment; -class Struct; -class Package; -struct TranslationUnit; - -class Module : public Importable -{ - Barriered code_; - Barriered globals_; - void *nativeIdentity_; - TranslationUnit *unit_; - Barriered imports_; - - public: - static Module *New(Zone *zone, Handle package, Handle path, Handle name); - - Code *code() { - return code_; - } - Struct *globals() { - return globals_; - } - - bool compiled() { - return !!code_; - } - - // Modules are pre-created, and may have their intermediate compilation - // data attached while they are being compiled. - bool compiling() { - return !!unit_; - } - TranslationUnit *translationUnit() { - return unit_; - } - void setTranslationUnit(TranslationUnit *unit) { - unit_ = unit; - } - - unsigned imports() { - return imports_ ? imports_->length() / 2 : 0; - } - Importable *getImportAt(unsigned index) { - return Importable::cast(imports_->at(index * 2)); - } - FixedArray *importPathAt(unsigned index) { - return FixedArray::cast(imports_->at(index * 2 + 1)); - } - void setImports(Handle imports) { - imports_ = imports; - } - void setCode(Handle code) { - code_ = code; - } - void setGlobals(Handle globals) { - globals_ = globals; - } - - // Begin GC Descriptor. - static inline size_t offsetOfCode() { - return OFFSETOF(Module, code_); - } - static inline size_t offsetOfGlobals() { - return OFFSETOF(Module, globals_); - } - static inline size_t offsetOfImports() { - return OFFSETOF(Module, imports_); - } - // End GC Descriptor. - - static inline Module *cast(Object *obj) { - assert(!obj || obj->is(MapKind_Module)); - return reinterpret_cast(obj); - } -}; - -} - -#endif // _include_jc_module_h_ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jc_module_h_ +#define _include_jc_module_h_ + +#include "Heap.h" +#include "Code.h" +#include "Functions.h" +#include "FixedArray.h" +#include "Packages.h" + +namespace ke { + +class Environment; +class Struct; +class Package; +struct TranslationUnit; + +class Module : public Importable +{ + Barriered code_; + Barriered globals_; + void *nativeIdentity_; + TranslationUnit *unit_; + Barriered imports_; + + public: + static Module *New(Zone *zone, Handle package, Handle path, Handle name); + + Code *code() { + return code_; + } + Struct *globals() { + return globals_; + } + + bool compiled() { + return !!code_; + } + + // Modules are pre-created, and may have their intermediate compilation + // data attached while they are being compiled. + bool compiling() { + return !!unit_; + } + TranslationUnit *translationUnit() { + return unit_; + } + void setTranslationUnit(TranslationUnit *unit) { + unit_ = unit; + } + + unsigned imports() { + return imports_ ? imports_->length() / 2 : 0; + } + Importable *getImportAt(unsigned index) { + return Importable::cast(imports_->at(index * 2)); + } + FixedArray *importPathAt(unsigned index) { + return FixedArray::cast(imports_->at(index * 2 + 1)); + } + void setImports(Handle imports) { + imports_ = imports; + } + void setCode(Handle code) { + code_ = code; + } + void setGlobals(Handle globals) { + globals_ = globals; + } + + // Begin GC Descriptor. + static inline size_t offsetOfCode() { + return OFFSETOF(Module, code_); + } + static inline size_t offsetOfGlobals() { + return OFFSETOF(Module, globals_); + } + static inline size_t offsetOfImports() { + return OFFSETOF(Module, imports_); + } + // End GC Descriptor. + + static inline Module *cast(Object *obj) { + assert(!obj || obj->is(MapKind_Module)); + return reinterpret_cast(obj); + } +}; + +} + +#endif // _include_jc_module_h_ diff -r c4b1341297e5 -r ba85a47ee414 src/NativeTable.cpp --- a/src/NativeTable.cpp Mon Jun 10 00:46:29 2013 +0100 +++ b/src/NativeTable.cpp Mon Jun 10 01:04:43 2013 +0100 @@ -1,97 +1,97 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#include "Zone.h" -#include "NativeTable.h" -#include "StringPool.h" -#include "Heap-inl.h" - -using namespace ke; - -NativeTable::NativeTable(Zone *zone) - : zone_(zone), - nativeMap_(ZoneAllocatorPolicy()) -{ -} - -NativeTable::~NativeTable() -{ - for (size_t i = 0; i < natives_.length(); i++) - delete natives_[i]; -} - -bool -NativeTable::initialize() -{ - return nativeMap_.init(16); -} - -bool -NativeTable::add(Handle name, size_t *indexp) -{ - // :TODO: assert no GC - CharsAndLength key(name->chars(), name->length()); - NativeMap::Insert p = nativeMap_.findForAdd(key); - if (p.found()) { - *indexp = (*p)->index; - return true; - } - - NativeInfo *info = new NativeInfo(name, NULL, natives_.length()); - if (!info || !natives_.append(info)) - return false; - - if (!nativeMap_.add(p, info)) - return false; - - *indexp = info->index; - - return true; -} - -bool -NativeTable::bind(const char *name, void *address) -{ - CharsAndLength key(name, strlen(name)); - NativeMap::Insert p = nativeMap_.findForAdd(key); - if (p.found()) { - (*p)->address = address; - return true; - } - - Local str(zone_, zone_->symbols()->add(key.str(), key.length())); - if (!str) - return false; - - // :TODO: assert no GC - NativeInfo *info = new NativeInfo(str, address, natives_.length()); - if (!info || !natives_.append(info)) - return false; - - if (!nativeMap_.add(p, info)) - return false; - - return true; -} - -void -NativeTable::accept(VirtualObjectVisitor *visitor) -{ - for (size_t i = 0; i < natives_.length(); i++) - visitor->visit(natives_[i]->name.address()); -} +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#include "Zone.h" +#include "NativeTable.h" +#include "StringPool.h" +#include "Heap-inl.h" + +using namespace ke; + +NativeTable::NativeTable(Zone *zone) + : zone_(zone), + nativeMap_(ZoneAllocatorPolicy()) +{ +} + +NativeTable::~NativeTable() +{ + for (size_t i = 0; i < natives_.length(); i++) + delete natives_[i]; +} + +bool +NativeTable::initialize() +{ + return nativeMap_.init(16); +} + +bool +NativeTable::add(Handle name, size_t *indexp) +{ + // :TODO: assert no GC + CharsAndLength key(name->chars(), name->length()); + NativeMap::Insert p = nativeMap_.findForAdd(key); + if (p.found()) { + *indexp = (*p)->index; + return true; + } + + NativeInfo *info = new NativeInfo(name, NULL, natives_.length()); + if (!info || !natives_.append(info)) + return false; + + if (!nativeMap_.add(p, info)) + return false; + + *indexp = info->index; + + return true; +} + +bool +NativeTable::bind(const char *name, void *address) +{ + CharsAndLength key(name, strlen(name)); + NativeMap::Insert p = nativeMap_.findForAdd(key); + if (p.found()) { + (*p)->address = address; + return true; + } + + Local str(zone_, zone_->symbols()->add(key.str(), key.length())); + if (!str) + return false; + + // :TODO: assert no GC + NativeInfo *info = new NativeInfo(str, address, natives_.length()); + if (!info || !natives_.append(info)) + return false; + + if (!nativeMap_.add(p, info)) + return false; + + return true; +} + +void +NativeTable::accept(VirtualObjectVisitor *visitor) +{ + for (size_t i = 0; i < natives_.length(); i++) + visitor->visit(natives_[i]->name.address()); +} diff -r c4b1341297e5 -r ba85a47ee414 src/NativeTable.h --- a/src/NativeTable.h Mon Jun 10 00:46:29 2013 +0100 +++ b/src/NativeTable.h Mon Jun 10 01:04:43 2013 +0100 @@ -1,90 +1,90 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#ifndef _include_sp2_native_table_h_ -#define _include_sp2_native_table_h_ - -#include -#include "AllocatorPolicies.h" -#include "Strings.h" -#include "HashTable.h" -#include "Vector.h" -#include "VirtualObjectVisitor.h" - -namespace ke { - -class NativeTable -{ - public: - NativeTable(Zone *zone); - ~NativeTable(); - - private: - struct NativeInfo { - Unbarriered name; - void *address; - size_t index; - - NativeInfo(String *name, void *address, size_t index) - : address(address), - index(index) - { - this->name = name; - } - }; - - struct Policy - { - typedef NativeInfo * Payload; - - static uint32 hash(const CharsAndLength &key) { - return HashCharSequence(key.str(), key.length()); - } - - static bool matches(const CharsAndLength &key, NativeInfo *info) { - if (info->name->length() != key.length()) - return false; - return memcmp(info->name->chars(), key.str(), key.length() * sizeof(char)) == 0; - } - }; - - typedef HashTable NativeMap; - - public: - bool initialize(); - - bool bind(const char *name, void *address); - bool add(Handle name, size_t *indexp); - String *name(size_t index) const { - return natives_[index]->name; - } - void *address(size_t index) const { - return natives_[index]->address; - } - - void accept(VirtualObjectVisitor *visitor); - - private: - Zone *zone_; - NativeMap nativeMap_; - Vector natives_; -}; - -} - -#endif // _include_sp2_native_table_h_ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_sp2_native_table_h_ +#define _include_sp2_native_table_h_ + +#include +#include "AllocatorPolicies.h" +#include "Strings.h" +#include "HashTable.h" +#include "Vector.h" +#include "VirtualObjectVisitor.h" + +namespace ke { + +class NativeTable +{ + public: + NativeTable(Zone *zone); + ~NativeTable(); + + private: + struct NativeInfo { + Unbarriered name; + void *address; + size_t index; + + NativeInfo(String *name, void *address, size_t index) + : address(address), + index(index) + { + this->name = name; + } + }; + + struct Policy + { + typedef NativeInfo * Payload; + + static uint32 hash(const CharsAndLength &key) { + return HashCharSequence(key.str(), key.length()); + } + + static bool matches(const CharsAndLength &key, NativeInfo *info) { + if (info->name->length() != key.length()) + return false; + return memcmp(info->name->chars(), key.str(), key.length() * sizeof(char)) == 0; + } + }; + + typedef HashTable NativeMap; + + public: + bool initialize(); + + bool bind(const char *name, void *address); + bool add(Handle name, size_t *indexp); + String *name(size_t index) const { + return natives_[index]->name; + } + void *address(size_t index) const { + return natives_[index]->address; + } + + void accept(VirtualObjectVisitor *visitor); + + private: + Zone *zone_; + NativeMap nativeMap_; + Vector natives_; +}; + +} + +#endif // _include_sp2_native_table_h_ diff -r c4b1341297e5 -r ba85a47ee414 src/OldByCo.cpp --- a/src/OldByCo.cpp Mon Jun 10 00:46:29 2013 +0100 +++ b/src/OldByCo.cpp Mon Jun 10 01:04:43 2013 +0100 @@ -1,1585 +1,1585 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of JITCraft. - * - * JITCraft is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * JITCraft. If not, see http://www.gnu.org/licenses/. - */ -#include -#include "../Modules.h" -#include "../Types.h" -#include "../Array.h" -#include "../Strings.h" -#include "../Zone.h" -#include "../Environments.h" -#include "../Box.h" -#include "../Heap-inl.h" -#include "BytecodeCompiler.h" -#include "Scopes.h" - -using namespace ke; - -typedef BytecodeEmitter::Label Label; - -#define __ emitter_. - -BytecodeCompiler::BytecodeCompiler(Zone *zone, ParseTree *tree, ModuleData *module) - : zone_(zone), - tree_(tree), - emitter_(zone->pool()), - locals_(NULL), - scope_(NULL), - moduleData_(module), - loop_(NULL) -{ -} - -static inline TokenKind -ToCompare(Expression *expr) -{ - if (expr->isBinaryExpression()) { - BinaryExpression *b = expr->toBinaryExpression(); - if (b->token() >= TOK_EQUALS && b->token() <= TOK_OR) - return b->token(); - } else if (expr->isUnaryExpression()) { - UnaryExpression *u = expr->toUnaryExpression(); - if (u->token() == TOK_NOT) - return TOK_NOT; - } - return TOK_EOF; -} - -static inline Opcode -CompareTokenToJump(TokenKind kind) -{ - switch (kind) { - case TOK_EQUALS: - return OP_JEQ; - case TOK_NOTEQUALS: - return OP_JNE; - case TOK_GT: - return OP_JG; - case TOK_GE: - return OP_JGE; - case TOK_LT: - return OP_JL; - default: - assert(kind == TOK_LE); - return OP_JLE; - } -} - -static inline Opcode -TokenToOpcode(TokenKind kind) -{ - switch (kind) { - case TOK_PLUS: - return OP_ADD; - case TOK_MINUS: - return OP_SUB; - case TOK_STAR: - return OP_MUL; - case TOK_SLASH: - return OP_DIV; - case TOK_PERCENT: - return OP_MOD; - case TOK_SHR: - return OP_SHR; - case TOK_USHR: - return OP_USHR; - case TOK_SHL: - return OP_SHL; - case TOK_BITAND: - return OP_BITAND; - case TOK_BITOR: - return OP_BITOR; - case TOK_GT: - return OP_GT; - case TOK_GE: - return OP_GE; - case TOK_LT: - return OP_LT; - case TOK_LE: - return OP_LE; - case TOK_EQUALS: - return OP_EQ; - case TOK_NOTEQUALS: - return OP_NE; - default: - assert(kind == TOK_BITXOR); - return OP_BITXOR; - } -} - -void -BytecodeCompiler::recordLifoSlot(Scope *scope) -{ - if (!scope) - return; - - if (Variable *var = scope->lifoSlot()) { - locals_->set(zone_, var->slot(), var->type()); - __ savelifo(var->slot()); - } -} - -void -BytecodeCompiler::unwindTo(Scope *scope) -{ - // Note: we only need to unwind to the outermost scope's LIFO slot. - Variable *var = NULL; - for (Scope *current = scope_; current != scope; current = current->enclosing()) { - if (Variable *slot = current->lifoSlot()) - var = slot; - } - - if (var) - __ unwindlifo(var->slot()); -} - -void -BytecodeCompiler::visit(NameProxy *name) -{ - switch (name->binding()->kind()) { - case BoundName::VARIABLE: - { - Variable *var = name->binding()->toVariable(); - - if (var->location() == Variable::STACK) { - __ getlocal(var); - assert(!name->type()->isReference()); - } else if (var->location() == Variable::ENVIRONMENT) { - getEnvSlot(var); - } else { - assert(var->location() == Variable::PARAMETER); - __ getarg(var); - - // If this argument is actually a reference, dereference it now. - if (var->type()->isReference()) - __ getref(var->type()->contained()); - } - break; - } - - case BoundName::FUNCTION: - { - NamedFunction *fun = name->binding()->toNamedFunction(); - __ module(); - __ getmodule_fn(fun->slot()); - break; - } - - default: - { - NamedConstant *constant = name->binding()->toNamedConstant(); - __ int_(constant->value().toInt()); - break; - } - } -} - -void -BytecodeCompiler::visit(ExpressionStatement *node) -{ - // Expressions leave their value on the top of the stack, so we make sure - // to balance the stack if they're used as a statement. - node->expression()->accept(this); - if (!node->expression()->type()->isVoid()) - __ pop(); -} - -static inline bool -IsCompositeValueType(Type *type) -{ - return type->isStruct() || - (type->isArray() && type->isFixedLength()); -} - -static inline Array * -StringLiteralToArray(Zone *zone, StringLiteral *node) -{ - Local type(zone, node->type()); - Local map(zone, ArrayMap::Attach(zone, type)); - if (!map) - return NULL; - - return Array::NewString(zone, - map, - node->string()->raw(), - node->string()->length(), - Heap::Tenure_Old); -} - -void -BytecodeCompiler::visit(Assignment *node) -{ - Local left(zone_, node->type()); - Local right(zone_, node->expression()->type()); - - // The first step is three sub-steps: - // - // (1) Bind the left-hand side. - // -- If a cumulative operation (+=, etc): - // (2) Save a copy of the binding on the stack. - // (3) Dereference the binding to get a value. - // - if (node->lvalue()->isNameProxy()) { - NameProxy *proxy = node->lvalue()->toNameProxy(); - Variable *var = proxy->binding()->toVariable(); - - // We need to fetch the variable under the following circumstances: - // (1) The variable is a reference, since we'll store into the ref. - // (2) The variable is a fixed array, since we need to copy into it. - // (3) The variable is a struct, since we need to copy into it. - if (var->type()->isReference() || IsCompositeValueType(var->type())) - __ get(var); - - // Get the value if needed. - if (node->token() != TOK_ASSIGN) { - // += etc is not allowed on arrays. - assert(!var->type()->isArray()); - - if (var->location() == Variable::ENVIRONMENT) - getEnvSlot(var); - - if (var->type()->isReference()) { - __ dup(); - __ getref(var->type()->contained()); - } else if (var->location() != Variable::ENVIRONMENT) { - __ get(var); - } - } - } else if (node->lvalue()->isFieldExpression()) { - FieldExpression *expr = node->lvalue()->toFieldExpression(); - __ note_position(expr->pos()); - - expr->left()->accept(this); - - if (node->token() != TOK_ASSIGN) - __ dup(); - - if (node->token() != TOK_ASSIGN || IsCompositeValueType(left)) - __ getfield(expr->fieldIndex(), left); - } else { - IndexExpression *index = node->lvalue()->toIndexExpression(); - __ note_position(index->pos()); - - index->left()->accept(this); - index->right()->accept(this); - - if (node->token() != TOK_ASSIGN) - __ dup2(); - - if (node->token() != TOK_ASSIGN || IsCompositeValueType(left)) - __ getelem(left); - } - - if (node->token() == TOK_ASSIGN) { - // Special case: if the left-hand side is a string literal, we must - // spawn a new copy (:TODO: something about const?) - if (node->expression()->isStringLiteral() && !left->isFixedLength()) { - Local string(zone_, - StringLiteralToArray(zone_, node->expression()->toStringLiteral())); - if (!string) - return; - - Local arrayMap(zone_, ArrayMap::cast(string->map())); - __ note_position(node->expression()->pos()); - __ int_(string->length()); - __ newarray(OP_NEWSIZED, arrayMap, Heap::Tenure_Default); - __ object(string); - __ copyarray(); - } else { - node->expression()->accept(this); - } - } else { - visitForALU(left, node->expression()); - - TokenKind alu = TokenKind(int(node->token()) - (TOK_ASSIGN_ADD - TOK_PLUS)); - __ note_position(node->pos()); - __ binary(TokenToOpcode(alu)); - } - - // Emit the store. - if (left->isArray() && left->isFixedLength()) { - // (x = y) returns the left-hand side; if |x| is a fixed-length array, - // then |y| is as well, and we perform a deep copy. - assert(right->isFixedLength()); - __ note_position(node->pos()); - __ copyarray(); - } else if (left->isStruct()) { - // Structs have value semantics, so emit a full copy. We don't break - // this down into u-ops, to avoid unnecessary bytecode explosion. - assert(right->isStruct()); - __ note_position(node->pos()); - __ copystruct(); - } else if (node->lvalue()->isNameProxy()) { - NameProxy *proxy = node->lvalue()->toNameProxy(); - Variable *var = proxy->binding()->toVariable(); - - if (var->type()->isReference()) - __ setref(var->type()->contained()); - else if (var->location() == Variable::ENVIRONMENT) - setEnvSlot(var); - else - __ set(var); - } else if (node->lvalue()->isFieldExpression()) { - FieldExpression *expr = node->lvalue()->toFieldExpression(); - __ setfield(expr->fieldIndex(), left); - } else { - __ setelem(left); - } -} - -void -BytecodeCompiler::visit(IncDecExpression *node) -{ - // In the postfix case, the logic looks like: - // (1) Bind -> REF - // (2) Dup -> REF REF - // (3) Fetch -> REF VAL - // (4) Swap -> VAL REF - // (5) Pick -> VAL REF VAL - // (6) Add -> VAL REF VAL+1 - // (7) Store -> VAL VAL+1 - // (8) Drop -> VAL - // - // In the prefix case, the logic looks like: - // (1) Bind -> REF - // (2) Dup -> REF REF - // (3) Fetch -> REF VAL - // -- - // -- - // (6) Add -> REF VAL+1 - // (7) Store -> VAL+1 - // -- - // - // This is squirrely enough such that we don't bother sharing too much - // with the above. - // - // Note: We simulate a "Pick" operation with a dup2+pop. - - // This is steps (1) to (5). - Expression *expr = node->expression(); - if (expr->isNameProxy()) { - NameProxy *proxy = expr->toNameProxy(); - Variable *var = proxy->binding()->toVariable(); - - // Bind. - if (var->location() == Variable::ENVIRONMENT) - getEnvSlot(var); - else if (var->type()->isReference() || var->type()->isArray()) - __ get(var); - - // Dup and fetch. - if (var->type()->isReference()) { - __ dup(); - __ getref(node->type()); - if (node->postfix()) { - __ swap(); - __ pick2(); - } - } else { - assert(!var->type()->isArray()); - if (var->location() != Variable::ENVIRONMENT) - __ get(var); - if (node->postfix()) - __ dup(); - } - } else if (expr->isFieldExpression()) { - FieldExpression *field = expr->toFieldExpression(); - - field->left()->accept(this); - __ dup(); - __ getfield(field->fieldIndex(), field->type()); - - if (node->postfix()) { - // The stack looks like this now: - // OBJECT VALUE - __ swap(); // VALUE OBJECT - __ pick2(); // VALUE OBJECT VALUE - } - } else { - IndexExpression *index = expr->toIndexExpression(); - __ note_position(index->pos()); - - index->left()->accept(this); - index->right()->accept(this); - - assert(!node->type()->isArray()); - __ dup2(); - __ getelem(node->type()); - - if (node->postfix()) { - // The stack looks like this now: - // ARRAY INDEX VALUE - // - // So we must use a PICK operation to rotate the stack into position. - __ roll3(); // INDEX VALUE ARRAY - __ roll3(); // VALUE ARRAY INDEX - __ pick3(); // VALUE ARRAY INDEX VALUE - } - } - - // Perform step (6). - if (node->type()->isFloat()) { - __ float_(1.0); - } else { - assert(node->type()->isInt32()); - __ int_(1); - } - - __ note_position(node->pos()); - if (node->token() == TOK_INCREMENT) - __ binary(OP_ADD); - else - __ binary(OP_SUB); - - // Perform step (7). - if (expr->isNameProxy()) { - NameProxy *proxy = expr->toNameProxy(); - Variable *var = proxy->binding()->toVariable(); - - if (var->type()->isReference()) - __ setref(node->type()); - else if (var->location() == Variable::ENVIRONMENT) - setEnvSlot(var); - else - __ set(var); - } else if (expr->isFieldExpression()) { - FieldExpression *field = expr->toFieldExpression(); - __ setfield(field->fieldIndex(), field->type()); - } else { - assert(expr->isIndexExpression()); - __ setelem(node->type()); - } - - // Perform step (8). - if (node->postfix()) - __ pop(); -} - -void -BytecodeCompiler::visitForALU(Type *needed, Expression *node) -{ - node->accept(this); - if (needed->isFloat() && !node->type()->isFloat()) - __ cvt_i2f(); - else - assert(needed->primitive() == node->type()->primitive()); -} - -static inline Type * -GetCoercionType(TokenKind tok, Type *left, Type *right) -{ - if (left->isFloat()) - return left; - return right; -} - -void -BytecodeCompiler::visit(UnaryExpression *node) -{ - Local out(zone_, ((Expression *)node)->type()); - - switch (node->token()) { - case TOK_MINUS: - { - Local type(zone_, node->expression()->type()); - node->expression()->accept(this); - - if (out->isFloat()) - __ neg_f(); - else - __ neg(); - break; - } - - case TOK_NOT: - { - Local type(zone_, node->expression()->type()); - node->expression()->accept(this); - - if (out->isFloat()) - __ not_f(); - else - __ not_(); - break; - } - - case TOK_LABEL: - { - Local from(zone_, node->expression()->type()); - - node->expression()->accept(this); - if (!from->isFloat() && out->isFloat()) - __ bits_f2i(); - else if (from->isFloat() && !out->isFloat()) - __ bits_i2f(); - break; - } - - case TOK_SIZEOF: - { - Local type(zone_, node->expression()->type()); - if (type->isFixedLength()) { - __ int_(type->fixedLength()); - } else { - node->expression()->accept(this); - __ sizeof_(); - } - break; - } - - default: - assert(node->token() == TOK_TILDE); - node->expression()->accept(this); - __ bitnot(); - break; - } -} - -void -BytecodeCompiler::visit(BinaryExpression *node) -{ - if (node->token() == TOK_AND || node->token() == TOK_OR) { - Label success, failure, done; - visitForTest(node, &success, &failure, &success); - __ bind(&success); - __ int_(1); - __ drop(1); // Otherwise, the stack depth will be +1 too much. - __ jump(&done); - __ bind(&failure); - __ int_(0); - __ bind(&done); - return; - } - - Local leftType(zone_, node->left()->type()); - Local rightType(zone_, node->right()->type()); - Local coerce(zone_, GetCoercionType(node->token(), leftType, rightType)); - - visitForALU(coerce, node->left()); - visitForALU(coerce, node->right()); - - Opcode op = TokenToOpcode(node->token()); - if (node->type()->isFloat()) - op = (Opcode)(int(op) + (OP_ADD_F - OP_ADD)); - - __ note_position(node->pos()); - __ binary(op); -} - -void -BytecodeCompiler::visit(IndexExpression *node) -{ - node->left()->accept(this); - node->right()->accept(this); - __ note_position(node->pos()); - __ getelem(node->type()); -} - -void -BytecodeCompiler::visit(FieldExpression *node) -{ - node->left()->accept(this); - __ getfield(node->fieldIndex(), node->type()); -} - -void -BytecodeCompiler::visit(ReturnStatement *node) -{ - if (node->expression()) { - node->expression()->accept(this); - __ return_(); - } else { - __ returnvoid(); - } -} - -void -BytecodeCompiler::visitForTest(Expression *expression, - Label *trueBranch, - Label *falseBranch, - Label *fallthrough) -{ - TokenKind token = ToCompare(expression); - if (token == TOK_EOF || token == TOK_NOT) { - if (token == TOK_EOF) - expression->accept(this); - else - expression->toUnaryExpression()->expression()->accept(this); - - bool jumpOnTrue = (fallthrough == falseBranch) ^ (token == TOK_NOT); - - Opcode op = jumpOnTrue ? OP_JT : OP_JF; - if (expression->type()->isFloat()) - op = (Opcode)(int(op) + (OP_JL_F - OP_JL)); - - if (fallthrough == falseBranch) - __ j(op, trueBranch); - else - __ j(op, falseBranch); - return; - } - - BinaryExpression *binary = expression->toBinaryExpression(); - - if (token == TOK_AND || token == TOK_OR) { - Label next; - if (token == TOK_AND) - visitForTest(binary->left(), &next, falseBranch, &next); - else - visitForTest(binary->left(), trueBranch, &next, &next); - __ bind(&next); - visitForTest(binary->right(), trueBranch, falseBranch, fallthrough); - return; - } - - Opcode op = CompareTokenToJump(token); - if (fallthrough == trueBranch) - op = Invert(op); - - Local leftType(zone_, binary->left()->type()); - Local rightType(zone_, binary->right()->type()); - Local coerce(zone_, GetCoercionType(binary->token(), leftType, rightType)); - - visitForALU(coerce, binary->left()); - visitForALU(coerce, binary->right()); - if (coerce->isFloat()) - op = (Opcode)(int(op) + (OP_JL_F - OP_JL)); - - if (fallthrough == trueBranch) - __ j(op, falseBranch); - else - __ j(op, trueBranch); -} - -void -BytecodeCompiler::visit(ForStatement *node) -{ - AutoEnterScope forScope(this, node->scope()); - - recordLifoSlot(node->scope()); - - Label join, update; - LoopScope loop(scope_, &join, &update, &loop_); - - if (node->initialization()) - node->initialization()->accept(this); - - Label header, test; - if (node->condition()) - __ jump(&test); - - __ bind(&header); - node->body()->accept(this); - - __ bind(&update); - if (node->update()) - node->update()->accept(this); - - if (node->condition()) { - __ bind(&test); - visitForTest(node->condition(), &header, &join, &join); - } else { - __ jump(&header); - } - - __ bind(&join); - - if (node->scope()) - unwindTo(node->scope()->enclosing()); - assert(__ stackDepth() == 0); -} - -void -BytecodeCompiler::visit(WhileStatement *node) -{ - Label header, join; - - __ bind(&header); - - LoopScope loop(scope_, &join, &header, &loop_); - - if (node->token() == TOK_WHILE) { - Label body; - - visitForTest(node->condition(), &body, &join, &body); - __ bind(&body); - node->body()->accept(this); - __ jump(&header); - } else { - node->body()->accept(this); - visitForTest(node->condition(), &header, &join, &join); - } - - __ bind(&join); -} - -void -BytecodeCompiler::visit(BreakStatement *node) -{ - unwindTo(loop_->scope()); - __ jump(loop_->break_()); -} - -void -BytecodeCompiler::visit(ContinueStatement *node) -{ - unwindTo(loop_->scope()); - __ jump(loop_->continue_()); -} - -void -BytecodeCompiler::visit(BlockStatement *node) -{ - AutoEnterScope blockScope(this, node->scope()); - - recordLifoSlot(node->scope()); - - for (size_t i = 0; i < node->statements()->length(); i++) { - Statement *statement = node->statements()->at(i); - statement->accept(this); - assert(__ stackDepth() == 0); - } - - // We don't bother emitting an "unwind" instruction for function scopes, - // since this is taken care of by the calling convention. - if (node->scope() && node->scope()->enclosing()->kind() != Scope::FUNCTION) - unwindTo(node->scope()->enclosing()); -} - -void -BytecodeCompiler::visit(IntegerLiteral *node) -{ - __ int_(node->value()); -} - -void -BytecodeCompiler::visit(BooleanLiteral *node) -{ - if (node->token() == TOK_TRUE) - __ true_(); - else - __ false_(); -} - -void -BytecodeCompiler::visit(FloatLiteral *node) -{ - __ float_((float)node->value()); -} - -void -BytecodeCompiler::visit(StringLiteral *node) -{ - Local array(zone_, StringLiteralToArray(zone_, node)); - if (!array) - return; - - __ note_position(node->pos()); - __ object(array); -} - -void -BytecodeCompiler::getEnvSlot(Variable *var) -{ - assert(var->scope()->kind() == Scope::GLOBAL); - __ getglobal(var); -} - -void -BytecodeCompiler::setEnvSlot(Variable *var) -{ - assert(var->scope()->kind() == Scope::GLOBAL); - __ setglobal(var); -} - -static bool -FillArrayLiteral_String(Zone *zone, Handle array, ArrayLiteral *literal, unsigned *maxsize) -{ - Local string(zone); - for (size_t i = 0; i < literal->expressions()->length(); i++) { - Expression *expr = literal->expressions()->at(i); - StringLiteral *lit = expr->toStringLiteral(); - if ((string = StringLiteralToArray(zone, lit)) == NULL) - return false; - - array->writeObject(zone, i, string); - - if (string->length() > *maxsize) - *maxsize = string->length(); - - assert(!array->type()->contained()->isFixedLength() || - array->type()->contained()->fixedLength() >= string->length()); - } - - return true; -} - -static bool -FillArrayLiteral_Primitive(Zone *zone, Handle array, ArrayLiteral *literal) -{ - assert(array->type()->fixedLength() == literal->expressions()->length()); - - for (size_t i = 0; i < literal->expressions()->length(); i++) { - Expression *expr = literal->expressions()->at(i); - if (IntegerLiteral *lit = expr->asIntegerLiteral()) { - array->writeInt32(i, lit->value()); - continue; - } - if (BooleanLiteral *lit = expr->asBooleanLiteral()) { - if (lit->token() == TOK_TRUE) - array->writeInt32(i, 1); - else - array->writeInt32(i, 0); - continue; - } - if (FloatLiteral *lit = expr->asFloatLiteral()) { - array->writeFloat(i, (float)lit->value()); - continue; - } - - // Unrecognized literal type. - assert(false); - } - - return true; -} - -static bool -TraverseArrayLiteral(Zone *zone, ArrayLiteral *literal, Handle array, Handle type, - unsigned *maxsize) -{ - assert(type->fixedLength() == literal->expressions()->length()); - - Local contained(zone, type->contained()); - if (contained->isCharArray()) - return FillArrayLiteral_String(zone, array, literal, maxsize); - - if (!contained->isArray()) - return FillArrayLiteral_Primitive(zone, array, literal); - - Local subarray(zone); - for (size_t i = 0; i < literal->expressions()->length(); i++) { - Expression *expr = literal->expressions()->at(i); - subarray = Array::cast(array->readObject(i)); - if (!TraverseArrayLiteral(zone, expr->toArrayLiteral(), subarray, contained, maxsize)) - return false; - } - - return true; -} - -static Array * -BuildArrayLiteral(Zone *zone, ArrayLiteral *literal, Handle map, unsigned *maxsize) -{ - Local root(zone, Array::NewFixed(zone, map, Heap::Tenure_Old)); - if (!root) - return NULL; - - // This should have allocated everything in the array, so all we need to - // do is traverse down to the lowest levels and copy things. - Local type(zone, map->type()); - if (!TraverseArrayLiteral(zone, literal, root, type, maxsize)) - return NULL; - - return root; -} - -static inline bool -ArrayContainsCharBuffers(Type *type) -{ - if (type->contained()->isArray()) - return ArrayContainsCharBuffers(type->contained()); - return type->isCharArray(); -} - -void -BytecodeCompiler::visit(VariableDeclaration *node) -{ - Variable *var = node->variable(); - - Local type(zone_, var->type()); - if (type->isArray()) { - Local map(zone_, ArrayMap::Attach(zone_, type)); - __ note_position(node->pos()); - - Local right(zone_); - if (node->initialization()) - right = node->initialization()->type(); - - // There are a few methods for allocating, here: - // (1) If the array is fixed, - // (a) Initializer not present, - // NEWFIXED - // (b) Left is const non-string: - // - // (b) Otherwise: - // NEWFIXED - // COPYARRAY - // (2) If the array is not fixed, - // (a) Initializer is not present, - // NEWEMPTY - // (b) Initializer is not a string literal, - // or left is const: - // - // (c) Initializer is a string literal, - // NEWSIZED - // COPYARRAY - // - - Heap::Tenure tenure = var->scope()->kind() == Scope::GLOBAL - ? Heap::Tenure_Old - : Heap::Tenure_Default; - - if (Expression *init = node->initialization()) { - if (init->isArrayLiteral()) { - unsigned maxsize = 0; - Local array(zone_, BuildArrayLiteral(zone_, init->toArrayLiteral(), map, &maxsize)); - if (!array) - return; - - assert(type->isFixedLength()); - - if (type->isConst()) { - __ object(array); - } else if (ArrayContainsCharBuffers(type)) { - // This array contains mutable char buffers, so - // unfortunately this gets a little more complicated. We - // compute the maximum size needed for the innermost - // string, which is similar to SP1. This means we can use - // CopyArray rather than individually emit opcodes to clone - // every string. - Local child(zone_, type); - for (size_t i = 0; i < type->levels() - 1; i++) { - __ int_(child->fixedLength()); - child = child->contained(); - } - __ int_(maxsize); - __ newarray(OP_NEWSIZED, map, tenure); - __ object(array); - __ copyarray(); - } else { - __ newarray(OP_NEWFIXED, map, tenure); - __ object(array); - __ copyarray(); - } - } else if (type->isFixedLength()) { - // Note in the case of: - // const String:buffer[50] = "hello"; - // - // We want to preserve sizeof(buffer) == 50. - if (type->isConst() && !type->isCharArray()) { - init->accept(this); - } else { - __ newarray(OP_NEWFIXED, map, tenure); - init->accept(this); - __ copyarray(); - } - } else if (!type->isConst() && init->isStringLiteral()) { - // This is a degenerate case of ArrayLiteral. - __ int_(right->fixedLength()); - __ newarray(OP_NEWSIZED, map, tenure); - init->accept(this); - __ copyarray(); - } else if (type->isConst()) { - init->accept(this); - } - } else if (type->isFixedLength()) { - __ newarray(OP_NEWFIXED, map, tenure); - } else { -#if 0 - if (node->postDimensions()) { - for (size_t i = 0; i < node->postDimensions()->length(); i++) { - Expression *expr = node->postDimensions()->at(i); - if (expr) - expr->accept(this); - else - __ int_(0); - } - __ newarray(OP_NEWSIZED, map, tenure); - } else { - __ newarray(OP_NEWEMPTY, map, tenure); - } -#endif - } - } else if (type->isStruct()) { - Local map(zone_, StructMap::cast(type->newMap())); - - // Because structs can contain arrays, any sort of composition - // triggers non-LIFO allocation. This is just for correctness. - // We could do better by: - // (1) Limiting to structs that just (recursively) contain arrays, - // (2) Swap Stack for Default tenure in Struct::New - // (3) JIT analysis - if (var->scope()->kind() == Scope::GLOBAL) - __ newstruct(map, Heap::Tenure_Old); - else if (var->needsHeapAllocation() || map->composed()) - __ newstruct(map, Heap::Tenure_Default); - else - __ newstruct(map, Heap::Tenure_Stack); - } else { - if (node->initialization()) { - node->initialization()->accept(this); - } else { - if (type->isInt32OrEnum() || type->isBool()) { - __ int_(0); - } else { - assert(type->isFloat()); - __ float_(0); - } - } - } - - if (var->location() == Variable::STACK) { - __ setlocal(var); - locals_->set(zone_, var->slot(), var->type()); - } else { - assert(var->location() == Variable::ENVIRONMENT); - setEnvSlot(var); - } - - __ pop(); -} - -void -BytecodeCompiler::visit(CallExpression *node) -{ -#if 0 - Local funType(zone_, node->callee()->type()); - - // Sniff out natives, which have a specific opcode and are guaranteed to - // be called via name proxy. - NamedFunction *native = NULL; - if (funType->isNative()) - native = node->callee()->toNameProxy()->binding()->toNamedFunction(); - - Local given(zone_); - for (size_t i = node->arguments()->length() - 1; i < node->arguments()->length(); i--) { - Expression *arg = node->arguments()->at(i); - - given = arg->type(); - - // We need to compute a reference if the parameter was in the form: - // f(&a) - // Note this form does not allow: f({}) - // - // We also need to compute references for all variadic native - // arguments, but arrays and references are already in the - // appropriate form. - bool mustPushReference = false; - if (i >= funType->parameters()->length()) { - assert(funType->isNative() && funType->isNativeVariadic()); - mustPushReference = !given->isArray() && !given->isReference(); - } else if (funType->parameterAt(i)->isReference()) { - mustPushReference = !given->isReference(); - } - - // If a native is passed to a native, it must only be one level deep. - assert(!funType->isNative() || !given->isArray() || given->levels() == 1); - - if (mustPushReference) { - if (arg->isNameProxy()) { - // :TODO: test with functions and globals - // Compute a reference to the name. - Variable *var = arg->toNameProxy()->binding()->toVariable(); - __ note_position(node->pos()); - __ newref(var); - } else if (arg->isFieldExpression()) { - FieldExpression *expr = arg->toFieldExpression(); - expr->left()->accept(this); - __ note_position(expr->pos()); - __ fieldref_lifo(expr->fieldIndex()); - } else { - // We don't know how to, or can't, build a direct reference to - // this type. Make an indirect one, by taking the top of the - // stack and boxing it up. - arg->accept(this); - __ note_position(node->pos()); - __ slotref_lifo(); - } - } else { - arg->accept(this); - } - } - - unsigned argc = node->arguments()->length(); - - // If there are missing arguments, then we're not done and have to push - // default parameters. - Local box(zone_); - Local array(zone_); - Local map(zone_); - for (size_t i = node->arguments()->length(); i < funType->parameters()->length(); i++) { - box = Box::cast(funType->defaults()->at(i)); - - if (box->type()->isPod()) { - if (box->type()->pod() == PrimitiveType_Int32) - __ int_(box->toInt32()); - else if (box->type()->pod() == PrimitiveType_Float) - __ float_(box->toFloat()); - else - assert(false); - - if (funType->parameterAt(i)->isReference()) { - __ note_position(node->pos()); - __ slotref_lifo(); - } - } else { - assert(box->type()->isArray()); - - array = Array::cast(box->toObject()); - if (funType->parameterAt(i)->isConst()) { - __ object(array); - } else { - // We need to copy the array since it's mutable. Note that - // although COPYARRAY returns its left-hand side, this may - // change in the future, we do the same thing and dupe the - // newly allocated array. - map = ArrayMap::cast(array->map()); - - // Natives cannot escape array references, so we can allocate - // them on the LIFO stack. - if (funType->isNative()) - __ newarray(OP_NEWFIXED, map, Heap::Tenure_Stack); - else - __ newarray(OP_NEWFIXED, map, Heap::Tenure_Default); - __ dup(); - __ object(array); - __ copyarray(); - __ pop(); - } - } - - argc++; - } - - __ note_position(node->pos()); - if (native) { - __ callnative(native->slot(), argc); - __ drop(argc); - } else { - node->callee()->accept(this); - __ call(funType); - __ drop(argc + 1); - } -#endif -} - -void -BytecodeCompiler::visit(TernaryExpression *node) -{ - Label success, failure, join; - - visitForTest(node->condition(), &success, &failure, &success); - __ bind(&success); - node->left()->accept(this); - __ drop(1); - __ jump(&join); - __ bind(&failure); - node->right()->accept(this); - __ bind(&join); -} - -void -BytecodeCompiler::visit(IfStatement *node) -{ - Label join; - if (!node->ifFalse()) { - Label success; - - visitForTest(node->condition(), &success, &join, &success); - __ bind(&success); - node->ifTrue()->accept(this); - } else { - Label success, next; - - visitForTest(node->condition(), &success, &next, &success); - __ bind(&success); - node->ifTrue()->accept(this); - __ jump(&join); - __ bind(&next); - node->ifFalse()->accept(this); - } - - __ bind(&join); -} - -bool -BytecodeCompiler::tableswitch(SwitchStatement *node) -{ - CaseValueList *values = node->caseValueList(); - - if (values->length() < 3) - return false; - - // Find the delta between the largest and smallest value. - int hi = values->at(values->length() - 1).box.toInt(); - int lo = values->at(0).box.toInt(); - assert(hi > lo); - - unsigned tablesize = TableSwitchEntries(lo, hi); - if (tablesize > MAX_TABLESWITCH_ENTRIES) - return false; - - Label def, join; - - // Create the list of labels that each statement will bind to. - Label *labels = new Label[node->cases()->length()]; - - // Create the table of labels we'll use for each case. - Label **table = new Label *[tablesize]; - - // Link all cases, by default, to the default label. - for (size_t i = 0; i < tablesize; i++) - table[i] = &def; - - // Now patch actual cases to their correct labels. - for (size_t i = 0; i < values->length(); i++) { - CaseValue &v = values->at(i); - assert(v.statement < node->cases()->length()); - - unsigned index = unsigned(v.box.toInt() - lo); - assert(index < tablesize); - - table[index] = &labels[v.statement]; - } - - // Emit the tableswitch opcode and the jump table. - __ tableswitch(lo, hi, &def, table); - - // We're done with the jump table now. - delete [] table; - - // Emit each statement. - for (size_t i = 0; i < node->cases()->length(); i++) { - Statement *stmt = node->cases()->at(i)->statement(); - - __ bind(&labels[i]); - stmt->accept(this); - __ jump(&join); - } - - // We're done with the statement labels. - delete [] labels; - - // Emit the default case. - __ bind(&def); - if (node->defaultCase()) - node->defaultCase()->accept(this); - - __ bind(&join); - - return true; -} - -void -BytecodeCompiler::visit(SwitchStatement *node) -{ - node->expression()->accept(this); - - if (tableswitch(node)) - return; - - Label join; - - for (size_t i = 0; i < node->cases()->length(); i++) { - Case *c = node->cases()->at(i); - - Label success, fail; - if (c->others()) { - for (size_t j = 0; j < c->others()->length(); j++) { - Expression *expr = c->others()->at(j); - - __ dup(); - expr->accept(this); - __ j(OP_JEQ, &success); - } - } - - // The final test. - __ dup(); - c->expression()->accept(this); - __ j(OP_JNE, &fail); - __ bind(&success); - c->statement()->accept(this); - __ jump(&join); - __ bind(&fail); - } - - if (node->defaultCase()) - node->defaultCase()->accept(this); - - __ pop(); - __ bind(&join); -} - -void -BytecodeCompiler::visit(FunctionStatement *node) -{ - if (!node->fun()->used()) - return; - - if (node->fun()->isNative()) { - assert(!moduleData_->natives->at(node->fun()->slot())); - - Local type(zone_, FunctionType::cast(node->fun()->type())); - Local name(zone_, node->fun()->name()); - Local native(zone_, Native::New(zone_, type, name)); - if (!native) - return; - - moduleData_->natives->set(zone_, node->fun()->slot(), native); - return; - } - - // If the function has no body, then it's just a declaration. - if (!node->body()) { - assert(node->fun()->isForward() || node->fun()->isPublic()); - return; - } - - BytecodeCompiler child(zone_, tree_, moduleData_); - Local fun(zone_, child.compileFunction(moduleData_, node)); - if (!fun) - return; - - assert(!moduleData_->module->getFunction(node->fun()->slot())); - moduleData_->module->setFunction(zone_, node->fun()->slot(), fun); -} - -void -BytecodeCompiler::visit(FunctionTypeStatement *node) -{ -} - -void -BytecodeCompiler::visit(EnumStatement *node) -{ -} - -void -BytecodeCompiler::visit(StructureStatement *node) -{ -} - -void -BytecodeCompiler::visit(ArrayLiteral *node) -{ - // We shouldn't reach these; they're handled manually when initializing - // declared arrays. - assert(false); -} - -void -BytecodeCompiler::visit(ImportStatement *node) -{ -} - -Code * -BytecodeCompiler::compileCode(Scope *scope, unsigned lineno) -{ - // STOP doubles as RETURNVOID. - __ stop(); - - if (__ oom() || zone_->hasPendingException()) - return NULL; - - Local bytes(zone_, ByteArray::New(zone_, __ length() * sizeof(bytecode), Heap::Tenure_Old)); - if (!bytes) - return NULL; - memcpy(bytes->raw(), __ buffer(), __ length()); - - for (size_t i = 0; i < scope->maxContainedLocals(); i++) - assert(locals_->at(i)); - - Local objects(zone_, FixedArray::New(zone_, __ objects()->length(), Heap::Tenure_Old)); - if (!objects) - return NULL; - - for (size_t i = 0; i < __ objects()->length(); i++) - objects->set(zone_, i, *(__ objects()->at(i))); - - Local safepoints(zone_); - - // If any stack slots hold gc pointers at potential failure points in the - // bytecode, record them now. We do this by building a list of tuples; - // each tuple contains the pc offset, and a bitset containing which stack - // slots must be scanned by the gc. - if (__ oopRecords()->length()) { - size_t bytesPerSafepoint = Code::BytesPerSafepoint(__ maxStackDepth()); - size_t gcmapSize = bytesPerSafepoint * __ oopRecords()->length(); - safepoints = ByteArray::New(zone_, gcmapSize, Heap::Tenure_Old); - if (!safepoints) - return NULL; - - Address cursor = safepoints->raw(); - for (size_t i = 0; i < __ oopRecords()->length(); i++) { - OopRecord *record = __ oopRecords()->at(i); - assert(record->depth <= __ maxStackDepth()); - - *reinterpret_cast(cursor) = record->offset; - - unsigned char *bits = reinterpret_cast(cursor + sizeof(unsigned)); - for (unsigned i = 0; i < record->depth; i++) - bits[i / 8] |= (1 << (i % 8)); - - cursor += bytesPerSafepoint; - } - - assert(cursor == safepoints->raw() + gcmapSize); - } - - Local sourcemap(zone_); - if (__ sourcemapLength()) { - sourcemap = ByteArray::New(zone_, __ sourcemapLength(), Heap::Tenure_Old); - if (!sourcemap) - return NULL; - - memcpy(sourcemap->raw(), __ sourcemap(), __ sourcemapLength()); - } - - Local code(zone_); - - code = Code::New(zone_, - bytes, - locals_, - objects, - __ maxStackDepth(), - moduleData_->module, - safepoints, - sourcemap, - lineno); - return code; -} - -Function * -BytecodeCompiler::compileFunction(ModuleData *module, FunctionStatement *node) -{ - // Note: No need to record a life slot here, since it's part of the Frame. - AutoEnterScope functionScope(this, node->scope()); - - // Set the initial line, because line mappings are deltas. - __ setFirstLine(node->pos().line); - locals_ = FixedArray::New(zone_, node->scope()->maxContainedLocals(), Heap::Tenure_Old); - - node->body()->accept(this); - - unsigned nargs = node->scope()->numArguments(); - Local args(zone_, FixedArray::New(zone_, nargs, Heap::Tenure_Old)); - if (!args) - return NULL; - - for (size_t i = 0; i < node->scope()->numArguments(); i++) - args->set(zone_, i, node->scope()->getArgument(i)->type()); - - - Local code(zone_, compileCode(node->scope(), node->pos().line)); - if (!code) - return NULL; - - Local type(zone_, FunctionType::cast(node->fun()->type())); - Local name(zone_, node->fun()->name()); - Local fun(zone_, Function::New(zone_, code, type, name, node->pos())); - if (!fun) - return NULL; - - // If this function had a forward, export it as a named public. - if (node->fun()->isPublic()) { - assert(node->fun()->isPublic()); - ScopedRoot root(fun); - if (!root) - return NULL; - if (!moduleData_->publics.append(root)) - return NULL; - } - -#ifndef NDEBUG - Disassemble(code, stdout); -#endif - - return fun; -} - -Module * -BytecodeCompiler::compile() -{ - AutoEnterScope scope(this, tree_->globalScope()); - - ModuleData moduleData; - - moduleData_ = &moduleData; - moduleData_->module = Module::New(zone_, tree_->globalScope()->numFunctions()); - - if (!moduleData_) - return NULL; - - moduleData_->natives = FixedArray::New(zone_, tree_->globalScope()->numNatives(), Heap::Tenure_Old); - if (!moduleData_->natives) - return NULL; - - for (size_t i = 0; i < tree_->statements()->length(); i++) { - Statement *statement = tree_->statements()->at(i); - statement->accept(this); - } - - if (zone_->hasPendingException()) - return NULL; - -#ifndef NDEBUG - for (size_t i = 0; i < tree_->globalScope()->numFunctions(); i++) - assert(moduleData_->module->getFunction(i)); - for (size_t i = 0; i < tree_->globalScope()->numNatives(); i++) - assert(moduleData_->natives->at(i)); -#endif - - if (moduleData_->publics.length()) { - Local fun(zone_); - Local list(zone_, FixedArray::New(zone_, moduleData_->publics.length(), - Heap::Tenure_Old)); - if (!list) - return NULL; - - for (size_t i = 0; i < moduleData_->publics.length(); i++) - list->set(zone_, i, moduleData_->publics[i]); - - moduleData_->module->setPublics(list); - } - - moduleData_->module->setNatives(moduleData_->natives); - - if (scope_->numEnvironmentVars()) { - Local globalMap(zone_, scope_->createEnvironmentMap(zone_)); - if (!globalMap) - return NULL; - - Local environment(zone_, Environment::New(zone_, globalMap)); - if (!environment) - return NULL; - - moduleData_->module->setEnvironment(environment); - } - - Local code(zone_, compileCode(tree_->globalScope(), 1)); - if (!code) - return NULL; - - Disassemble(code, stdout); - - moduleData_->module->setCode(code); - - return moduleData_->module; -} +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include +#include "../Modules.h" +#include "../Types.h" +#include "../Array.h" +#include "../Strings.h" +#include "../Zone.h" +#include "../Environments.h" +#include "../Box.h" +#include "../Heap-inl.h" +#include "BytecodeCompiler.h" +#include "Scopes.h" + +using namespace ke; + +typedef BytecodeEmitter::Label Label; + +#define __ emitter_. + +BytecodeCompiler::BytecodeCompiler(Zone *zone, ParseTree *tree, ModuleData *module) + : zone_(zone), + tree_(tree), + emitter_(zone->pool()), + locals_(NULL), + scope_(NULL), + moduleData_(module), + loop_(NULL) +{ +} + +static inline TokenKind +ToCompare(Expression *expr) +{ + if (expr->isBinaryExpression()) { + BinaryExpression *b = expr->toBinaryExpression(); + if (b->token() >= TOK_EQUALS && b->token() <= TOK_OR) + return b->token(); + } else if (expr->isUnaryExpression()) { + UnaryExpression *u = expr->toUnaryExpression(); + if (u->token() == TOK_NOT) + return TOK_NOT; + } + return TOK_EOF; +} + +static inline Opcode +CompareTokenToJump(TokenKind kind) +{ + switch (kind) { + case TOK_EQUALS: + return OP_JEQ; + case TOK_NOTEQUALS: + return OP_JNE; + case TOK_GT: + return OP_JG; + case TOK_GE: + return OP_JGE; + case TOK_LT: + return OP_JL; + default: + assert(kind == TOK_LE); + return OP_JLE; + } +} + +static inline Opcode +TokenToOpcode(TokenKind kind) +{ + switch (kind) { + case TOK_PLUS: + return OP_ADD; + case TOK_MINUS: + return OP_SUB; + case TOK_STAR: + return OP_MUL; + case TOK_SLASH: + return OP_DIV; + case TOK_PERCENT: + return OP_MOD; + case TOK_SHR: + return OP_SHR; + case TOK_USHR: + return OP_USHR; + case TOK_SHL: + return OP_SHL; + case TOK_BITAND: + return OP_BITAND; + case TOK_BITOR: + return OP_BITOR; + case TOK_GT: + return OP_GT; + case TOK_GE: + return OP_GE; + case TOK_LT: + return OP_LT; + case TOK_LE: + return OP_LE; + case TOK_EQUALS: + return OP_EQ; + case TOK_NOTEQUALS: + return OP_NE; + default: + assert(kind == TOK_BITXOR); + return OP_BITXOR; + } +} + +void +BytecodeCompiler::recordLifoSlot(Scope *scope) +{ + if (!scope) + return; + + if (Variable *var = scope->lifoSlot()) { + locals_->set(zone_, var->slot(), var->type()); + __ savelifo(var->slot()); + } +} + +void +BytecodeCompiler::unwindTo(Scope *scope) +{ + // Note: we only need to unwind to the outermost scope's LIFO slot. + Variable *var = NULL; + for (Scope *current = scope_; current != scope; current = current->enclosing()) { + if (Variable *slot = current->lifoSlot()) + var = slot; + } + + if (var) + __ unwindlifo(var->slot()); +} + +void +BytecodeCompiler::visit(NameProxy *name) +{ + switch (name->binding()->kind()) { + case BoundName::VARIABLE: + { + Variable *var = name->binding()->toVariable(); + + if (var->location() == Variable::STACK) { + __ getlocal(var); + assert(!name->type()->isReference()); + } else if (var->location() == Variable::ENVIRONMENT) { + getEnvSlot(var); + } else { + assert(var->location() == Variable::PARAMETER); + __ getarg(var); + + // If this argument is actually a reference, dereference it now. + if (var->type()->isReference()) + __ getref(var->type()->contained()); + } + break; + } + + case BoundName::FUNCTION: + { + NamedFunction *fun = name->binding()->toNamedFunction(); + __ module(); + __ getmodule_fn(fun->slot()); + break; + } + + default: + { + NamedConstant *constant = name->binding()->toNamedConstant(); + __ int_(constant->value().toInt()); + break; + } + } +} + +void +BytecodeCompiler::visit(ExpressionStatement *node) +{ + // Expressions leave their value on the top of the stack, so we make sure + // to balance the stack if they're used as a statement. + node->expression()->accept(this); + if (!node->expression()->type()->isVoid()) + __ pop(); +} + +static inline bool +IsCompositeValueType(Type *type) +{ + return type->isStruct() || + (type->isArray() && type->isFixedLength()); +} + +static inline Array * +StringLiteralToArray(Zone *zone, StringLiteral *node) +{ + Local type(zone, node->type()); + Local map(zone, ArrayMap::Attach(zone, type)); + if (!map) + return NULL; + + return Array::NewString(zone, + map, + node->string()->raw(), + node->string()->length(), + Heap::Tenure_Old); +} + +void +BytecodeCompiler::visit(Assignment *node) +{ + Local left(zone_, node->type()); + Local right(zone_, node->expression()->type()); + + // The first step is three sub-steps: + // + // (1) Bind the left-hand side. + // -- If a cumulative operation (+=, etc): + // (2) Save a copy of the binding on the stack. + // (3) Dereference the binding to get a value. + // + if (node->lvalue()->isNameProxy()) { + NameProxy *proxy = node->lvalue()->toNameProxy(); + Variable *var = proxy->binding()->toVariable(); + + // We need to fetch the variable under the following circumstances: + // (1) The variable is a reference, since we'll store into the ref. + // (2) The variable is a fixed array, since we need to copy into it. + // (3) The variable is a struct, since we need to copy into it. + if (var->type()->isReference() || IsCompositeValueType(var->type())) + __ get(var); + + // Get the value if needed. + if (node->token() != TOK_ASSIGN) { + // += etc is not allowed on arrays. + assert(!var->type()->isArray()); + + if (var->location() == Variable::ENVIRONMENT) + getEnvSlot(var); + + if (var->type()->isReference()) { + __ dup(); + __ getref(var->type()->contained()); + } else if (var->location() != Variable::ENVIRONMENT) { + __ get(var); + } + } + } else if (node->lvalue()->isFieldExpression()) { + FieldExpression *expr = node->lvalue()->toFieldExpression(); + __ note_position(expr->pos()); + + expr->left()->accept(this); + + if (node->token() != TOK_ASSIGN) + __ dup(); + + if (node->token() != TOK_ASSIGN || IsCompositeValueType(left)) + __ getfield(expr->fieldIndex(), left); + } else { + IndexExpression *index = node->lvalue()->toIndexExpression(); + __ note_position(index->pos()); + + index->left()->accept(this); + index->right()->accept(this); + + if (node->token() != TOK_ASSIGN) + __ dup2(); + + if (node->token() != TOK_ASSIGN || IsCompositeValueType(left)) + __ getelem(left); + } + + if (node->token() == TOK_ASSIGN) { + // Special case: if the left-hand side is a string literal, we must + // spawn a new copy (:TODO: something about const?) + if (node->expression()->isStringLiteral() && !left->isFixedLength()) { + Local string(zone_, + StringLiteralToArray(zone_, node->expression()->toStringLiteral())); + if (!string) + return; + + Local arrayMap(zone_, ArrayMap::cast(string->map())); + __ note_position(node->expression()->pos()); + __ int_(string->length()); + __ newarray(OP_NEWSIZED, arrayMap, Heap::Tenure_Default); + __ object(string); + __ copyarray(); + } else { + node->expression()->accept(this); + } + } else { + visitForALU(left, node->expression()); + + TokenKind alu = TokenKind(int(node->token()) - (TOK_ASSIGN_ADD - TOK_PLUS)); + __ note_position(node->pos()); + __ binary(TokenToOpcode(alu)); + } + + // Emit the store. + if (left->isArray() && left->isFixedLength()) { + // (x = y) returns the left-hand side; if |x| is a fixed-length array, + // then |y| is as well, and we perform a deep copy. + assert(right->isFixedLength()); + __ note_position(node->pos()); + __ copyarray(); + } else if (left->isStruct()) { + // Structs have value semantics, so emit a full copy. We don't break + // this down into u-ops, to avoid unnecessary bytecode explosion. + assert(right->isStruct()); + __ note_position(node->pos()); + __ copystruct(); + } else if (node->lvalue()->isNameProxy()) { + NameProxy *proxy = node->lvalue()->toNameProxy(); + Variable *var = proxy->binding()->toVariable(); + + if (var->type()->isReference()) + __ setref(var->type()->contained()); + else if (var->location() == Variable::ENVIRONMENT) + setEnvSlot(var); + else + __ set(var); + } else if (node->lvalue()->isFieldExpression()) { + FieldExpression *expr = node->lvalue()->toFieldExpression(); + __ setfield(expr->fieldIndex(), left); + } else { + __ setelem(left); + } +} + +void +BytecodeCompiler::visit(IncDecExpression *node) +{ + // In the postfix case, the logic looks like: + // (1) Bind -> REF + // (2) Dup -> REF REF + // (3) Fetch -> REF VAL + // (4) Swap -> VAL REF + // (5) Pick -> VAL REF VAL + // (6) Add -> VAL REF VAL+1 + // (7) Store -> VAL VAL+1 + // (8) Drop -> VAL + // + // In the prefix case, the logic looks like: + // (1) Bind -> REF + // (2) Dup -> REF REF + // (3) Fetch -> REF VAL + // -- + // -- + // (6) Add -> REF VAL+1 + // (7) Store -> VAL+1 + // -- + // + // This is squirrely enough such that we don't bother sharing too much + // with the above. + // + // Note: We simulate a "Pick" operation with a dup2+pop. + + // This is steps (1) to (5). + Expression *expr = node->expression(); + if (expr->isNameProxy()) { + NameProxy *proxy = expr->toNameProxy(); + Variable *var = proxy->binding()->toVariable(); + + // Bind. + if (var->location() == Variable::ENVIRONMENT) + getEnvSlot(var); + else if (var->type()->isReference() || var->type()->isArray()) + __ get(var); + + // Dup and fetch. + if (var->type()->isReference()) { + __ dup(); + __ getref(node->type()); + if (node->postfix()) { + __ swap(); + __ pick2(); + } + } else { + assert(!var->type()->isArray()); + if (var->location() != Variable::ENVIRONMENT) + __ get(var); + if (node->postfix()) + __ dup(); + } + } else if (expr->isFieldExpression()) { + FieldExpression *field = expr->toFieldExpression(); + + field->left()->accept(this); + __ dup(); + __ getfield(field->fieldIndex(), field->type()); + + if (node->postfix()) { + // The stack looks like this now: + // OBJECT VALUE + __ swap(); // VALUE OBJECT + __ pick2(); // VALUE OBJECT VALUE + } + } else { + IndexExpression *index = expr->toIndexExpression(); + __ note_position(index->pos()); + + index->left()->accept(this); + index->right()->accept(this); + + assert(!node->type()->isArray()); + __ dup2(); + __ getelem(node->type()); + + if (node->postfix()) { + // The stack looks like this now: + // ARRAY INDEX VALUE + // + // So we must use a PICK operation to rotate the stack into position. + __ roll3(); // INDEX VALUE ARRAY + __ roll3(); // VALUE ARRAY INDEX + __ pick3(); // VALUE ARRAY INDEX VALUE + } + } + + // Perform step (6). + if (node->type()->isFloat()) { + __ float_(1.0); + } else { + assert(node->type()->isInt32()); + __ int_(1); + } + + __ note_position(node->pos()); + if (node->token() == TOK_INCREMENT) + __ binary(OP_ADD); + else + __ binary(OP_SUB); + + // Perform step (7). + if (expr->isNameProxy()) { + NameProxy *proxy = expr->toNameProxy(); + Variable *var = proxy->binding()->toVariable(); + + if (var->type()->isReference()) + __ setref(node->type()); + else if (var->location() == Variable::ENVIRONMENT) + setEnvSlot(var); + else + __ set(var); + } else if (expr->isFieldExpression()) { + FieldExpression *field = expr->toFieldExpression(); + __ setfield(field->fieldIndex(), field->type()); + } else { + assert(expr->isIndexExpression()); + __ setelem(node->type()); + } + + // Perform step (8). + if (node->postfix()) + __ pop(); +} + +void +BytecodeCompiler::visitForALU(Type *needed, Expression *node) +{ + node->accept(this); + if (needed->isFloat() && !node->type()->isFloat()) + __ cvt_i2f(); + else + assert(needed->primitive() == node->type()->primitive()); +} + +static inline Type * +GetCoercionType(TokenKind tok, Type *left, Type *right) +{ + if (left->isFloat()) + return left; + return right; +} + +void +BytecodeCompiler::visit(UnaryExpression *node) +{ + Local out(zone_, ((Expression *)node)->type()); + + switch (node->token()) { + case TOK_MINUS: + { + Local type(zone_, node->expression()->type()); + node->expression()->accept(this); + + if (out->isFloat()) + __ neg_f(); + else + __ neg(); + break; + } + + case TOK_NOT: + { + Local type(zone_, node->expression()->type()); + node->expression()->accept(this); + + if (out->isFloat()) + __ not_f(); + else + __ not_(); + break; + } + + case TOK_LABEL: + { + Local from(zone_, node->expression()->type()); + + node->expression()->accept(this); + if (!from->isFloat() && out->isFloat()) + __ bits_f2i(); + else if (from->isFloat() && !out->isFloat()) + __ bits_i2f(); + break; + } + + case TOK_SIZEOF: + { + Local type(zone_, node->expression()->type()); + if (type->isFixedLength()) { + __ int_(type->fixedLength()); + } else { + node->expression()->accept(this); + __ sizeof_(); + } + break; + } + + default: + assert(node->token() == TOK_TILDE); + node->expression()->accept(this); + __ bitnot(); + break; + } +} + +void +BytecodeCompiler::visit(BinaryExpression *node) +{ + if (node->token() == TOK_AND || node->token() == TOK_OR) { + Label success, failure, done; + visitForTest(node, &success, &failure, &success); + __ bind(&success); + __ int_(1); + __ drop(1); // Otherwise, the stack depth will be +1 too much. + __ jump(&done); + __ bind(&failure); + __ int_(0); + __ bind(&done); + return; + } + + Local leftType(zone_, node->left()->type()); + Local rightType(zone_, node->right()->type()); + Local coerce(zone_, GetCoercionType(node->token(), leftType, rightType)); + + visitForALU(coerce, node->left()); + visitForALU(coerce, node->right()); + + Opcode op = TokenToOpcode(node->token()); + if (node->type()->isFloat()) + op = (Opcode)(int(op) + (OP_ADD_F - OP_ADD)); + + __ note_position(node->pos()); + __ binary(op); +} + +void +BytecodeCompiler::visit(IndexExpression *node) +{ + node->left()->accept(this); + node->right()->accept(this); + __ note_position(node->pos()); + __ getelem(node->type()); +} + +void +BytecodeCompiler::visit(FieldExpression *node) +{ + node->left()->accept(this); + __ getfield(node->fieldIndex(), node->type()); +} + +void +BytecodeCompiler::visit(ReturnStatement *node) +{ + if (node->expression()) { + node->expression()->accept(this); + __ return_(); + } else { + __ returnvoid(); + } +} + +void +BytecodeCompiler::visitForTest(Expression *expression, + Label *trueBranch, + Label *falseBranch, + Label *fallthrough) +{ + TokenKind token = ToCompare(expression); + if (token == TOK_EOF || token == TOK_NOT) { + if (token == TOK_EOF) + expression->accept(this); + else + expression->toUnaryExpression()->expression()->accept(this); + + bool jumpOnTrue = (fallthrough == falseBranch) ^ (token == TOK_NOT); + + Opcode op = jumpOnTrue ? OP_JT : OP_JF; + if (expression->type()->isFloat()) + op = (Opcode)(int(op) + (OP_JL_F - OP_JL)); + + if (fallthrough == falseBranch) + __ j(op, trueBranch); + else + __ j(op, falseBranch); + return; + } + + BinaryExpression *binary = expression->toBinaryExpression(); + + if (token == TOK_AND || token == TOK_OR) { + Label next; + if (token == TOK_AND) + visitForTest(binary->left(), &next, falseBranch, &next); + else + visitForTest(binary->left(), trueBranch, &next, &next); + __ bind(&next); + visitForTest(binary->right(), trueBranch, falseBranch, fallthrough); + return; + } + + Opcode op = CompareTokenToJump(token); + if (fallthrough == trueBranch) + op = Invert(op); + + Local leftType(zone_, binary->left()->type()); + Local rightType(zone_, binary->right()->type()); + Local coerce(zone_, GetCoercionType(binary->token(), leftType, rightType)); + + visitForALU(coerce, binary->left()); + visitForALU(coerce, binary->right()); + if (coerce->isFloat()) + op = (Opcode)(int(op) + (OP_JL_F - OP_JL)); + + if (fallthrough == trueBranch) + __ j(op, falseBranch); + else + __ j(op, trueBranch); +} + +void +BytecodeCompiler::visit(ForStatement *node) +{ + AutoEnterScope forScope(this, node->scope()); + + recordLifoSlot(node->scope()); + + Label join, update; + LoopScope loop(scope_, &join, &update, &loop_); + + if (node->initialization()) + node->initialization()->accept(this); + + Label header, test; + if (node->condition()) + __ jump(&test); + + __ bind(&header); + node->body()->accept(this); + + __ bind(&update); + if (node->update()) + node->update()->accept(this); + + if (node->condition()) { + __ bind(&test); + visitForTest(node->condition(), &header, &join, &join); + } else { + __ jump(&header); + } + + __ bind(&join); + + if (node->scope()) + unwindTo(node->scope()->enclosing()); + assert(__ stackDepth() == 0); +} + +void +BytecodeCompiler::visit(WhileStatement *node) +{ + Label header, join; + + __ bind(&header); + + LoopScope loop(scope_, &join, &header, &loop_); + + if (node->token() == TOK_WHILE) { + Label body; + + visitForTest(node->condition(), &body, &join, &body); + __ bind(&body); + node->body()->accept(this); + __ jump(&header); + } else { + node->body()->accept(this); + visitForTest(node->condition(), &header, &join, &join); + } + + __ bind(&join); +} + +void +BytecodeCompiler::visit(BreakStatement *node) +{ + unwindTo(loop_->scope()); + __ jump(loop_->break_()); +} + +void +BytecodeCompiler::visit(ContinueStatement *node) +{ + unwindTo(loop_->scope()); + __ jump(loop_->continue_()); +} + +void +BytecodeCompiler::visit(BlockStatement *node) +{ + AutoEnterScope blockScope(this, node->scope()); + + recordLifoSlot(node->scope()); + + for (size_t i = 0; i < node->statements()->length(); i++) { + Statement *statement = node->statements()->at(i); + statement->accept(this); + assert(__ stackDepth() == 0); + } + + // We don't bother emitting an "unwind" instruction for function scopes, + // since this is taken care of by the calling convention. + if (node->scope() && node->scope()->enclosing()->kind() != Scope::FUNCTION) + unwindTo(node->scope()->enclosing()); +} + +void +BytecodeCompiler::visit(IntegerLiteral *node) +{ + __ int_(node->value()); +} + +void +BytecodeCompiler::visit(BooleanLiteral *node) +{ + if (node->token() == TOK_TRUE) + __ true_(); + else + __ false_(); +} + +void +BytecodeCompiler::visit(FloatLiteral *node) +{ + __ float_((float)node->value()); +} + +void +BytecodeCompiler::visit(StringLiteral *node) +{ + Local array(zone_, StringLiteralToArray(zone_, node)); + if (!array) + return; + + __ note_position(node->pos()); + __ object(array); +} + +void +BytecodeCompiler::getEnvSlot(Variable *var) +{ + assert(var->scope()->kind() == Scope::GLOBAL); + __ getglobal(var); +} + +void +BytecodeCompiler::setEnvSlot(Variable *var) +{ + assert(var->scope()->kind() == Scope::GLOBAL); + __ setglobal(var); +} + +static bool +FillArrayLiteral_String(Zone *zone, Handle array, ArrayLiteral *literal, unsigned *maxsize) +{ + Local string(zone); + for (size_t i = 0; i < literal->expressions()->length(); i++) { + Expression *expr = literal->expressions()->at(i); + StringLiteral *lit = expr->toStringLiteral(); + if ((string = StringLiteralToArray(zone, lit)) == NULL) + return false; + + array->writeObject(zone, i, string); + + if (string->length() > *maxsize) + *maxsize = string->length(); + + assert(!array->type()->contained()->isFixedLength() || + array->type()->contained()->fixedLength() >= string->length()); + } + + return true; +} + +static bool +FillArrayLiteral_Primitive(Zone *zone, Handle array, ArrayLiteral *literal) +{ + assert(array->type()->fixedLength() == literal->expressions()->length()); + + for (size_t i = 0; i < literal->expressions()->length(); i++) { + Expression *expr = literal->expressions()->at(i); + if (IntegerLiteral *lit = expr->asIntegerLiteral()) { + array->writeInt32(i, lit->value()); + continue; + } + if (BooleanLiteral *lit = expr->asBooleanLiteral()) { + if (lit->token() == TOK_TRUE) + array->writeInt32(i, 1); + else + array->writeInt32(i, 0); + continue; + } + if (FloatLiteral *lit = expr->asFloatLiteral()) { + array->writeFloat(i, (float)lit->value()); + continue; + } + + // Unrecognized literal type. + assert(false); + } + + return true; +} + +static bool +TraverseArrayLiteral(Zone *zone, ArrayLiteral *literal, Handle array, Handle type, + unsigned *maxsize) +{ + assert(type->fixedLength() == literal->expressions()->length()); + + Local contained(zone, type->contained()); + if (contained->isCharArray()) + return FillArrayLiteral_String(zone, array, literal, maxsize); + + if (!contained->isArray()) + return FillArrayLiteral_Primitive(zone, array, literal); + + Local subarray(zone); + for (size_t i = 0; i < literal->expressions()->length(); i++) { + Expression *expr = literal->expressions()->at(i); + subarray = Array::cast(array->readObject(i)); + if (!TraverseArrayLiteral(zone, expr->toArrayLiteral(), subarray, contained, maxsize)) + return false; + } + + return true; +} + +static Array * +BuildArrayLiteral(Zone *zone, ArrayLiteral *literal, Handle map, unsigned *maxsize) +{ + Local root(zone, Array::NewFixed(zone, map, Heap::Tenure_Old)); + if (!root) + return NULL; + + // This should have allocated everything in the array, so all we need to + // do is traverse down to the lowest levels and copy things. + Local type(zone, map->type()); + if (!TraverseArrayLiteral(zone, literal, root, type, maxsize)) + return NULL; + + return root; +} + +static inline bool +ArrayContainsCharBuffers(Type *type) +{ + if (type->contained()->isArray()) + return ArrayContainsCharBuffers(type->contained()); + return type->isCharArray(); +} + +void +BytecodeCompiler::visit(VariableDeclaration *node) +{ + Variable *var = node->variable(); + + Local type(zone_, var->type()); + if (type->isArray()) { + Local map(zone_, ArrayMap::Attach(zone_, type)); + __ note_position(node->pos()); + + Local right(zone_); + if (node->initialization()) + right = node->initialization()->type(); + + // There are a few methods for allocating, here: + // (1) If the array is fixed, + // (a) Initializer not present, + // NEWFIXED + // (b) Left is const non-string: + // + // (b) Otherwise: + // NEWFIXED + // COPYARRAY + // (2) If the array is not fixed, + // (a) Initializer is not present, + // NEWEMPTY + // (b) Initializer is not a string literal, + // or left is const: + // + // (c) Initializer is a string literal, + // NEWSIZED + // COPYARRAY + // + + Heap::Tenure tenure = var->scope()->kind() == Scope::GLOBAL + ? Heap::Tenure_Old + : Heap::Tenure_Default; + + if (Expression *init = node->initialization()) { + if (init->isArrayLiteral()) { + unsigned maxsize = 0; + Local array(zone_, BuildArrayLiteral(zone_, init->toArrayLiteral(), map, &maxsize)); + if (!array) + return; + + assert(type->isFixedLength()); + + if (type->isConst()) { + __ object(array); + } else if (ArrayContainsCharBuffers(type)) { + // This array contains mutable char buffers, so + // unfortunately this gets a little more complicated. We + // compute the maximum size needed for the innermost + // string, which is similar to SP1. This means we can use + // CopyArray rather than individually emit opcodes to clone + // every string. + Local child(zone_, type); + for (size_t i = 0; i < type->levels() - 1; i++) { + __ int_(child->fixedLength()); + child = child->contained(); + } + __ int_(maxsize); + __ newarray(OP_NEWSIZED, map, tenure); + __ object(array); + __ copyarray(); + } else { + __ newarray(OP_NEWFIXED, map, tenure); + __ object(array); + __ copyarray(); + } + } else if (type->isFixedLength()) { + // Note in the case of: + // const String:buffer[50] = "hello"; + // + // We want to preserve sizeof(buffer) == 50. + if (type->isConst() && !type->isCharArray()) { + init->accept(this); + } else { + __ newarray(OP_NEWFIXED, map, tenure); + init->accept(this); + __ copyarray(); + } + } else if (!type->isConst() && init->isStringLiteral()) { + // This is a degenerate case of ArrayLiteral. + __ int_(right->fixedLength()); + __ newarray(OP_NEWSIZED, map, tenure); + init->accept(this); + __ copyarray(); + } else if (type->isConst()) { + init->accept(this); + } + } else if (type->isFixedLength()) { + __ newarray(OP_NEWFIXED, map, tenure); + } else { +#if 0 + if (node->postDimensions()) { + for (size_t i = 0; i < node->postDimensions()->length(); i++) { + Expression *expr = node->postDimensions()->at(i); + if (expr) + expr->accept(this); + else + __ int_(0); + } + __ newarray(OP_NEWSIZED, map, tenure); + } else { + __ newarray(OP_NEWEMPTY, map, tenure); + } +#endif + } + } else if (type->isStruct()) { + Local map(zone_, StructMap::cast(type->newMap())); + + // Because structs can contain arrays, any sort of composition + // triggers non-LIFO allocation. This is just for correctness. + // We could do better by: + // (1) Limiting to structs that just (recursively) contain arrays, + // (2) Swap Stack for Default tenure in Struct::New + // (3) JIT analysis + if (var->scope()->kind() == Scope::GLOBAL) + __ newstruct(map, Heap::Tenure_Old); + else if (var->needsHeapAllocation() || map->composed()) + __ newstruct(map, Heap::Tenure_Default); + else + __ newstruct(map, Heap::Tenure_Stack); + } else { + if (node->initialization()) { + node->initialization()->accept(this); + } else { + if (type->isInt32OrEnum() || type->isBool()) { + __ int_(0); + } else { + assert(type->isFloat()); + __ float_(0); + } + } + } + + if (var->location() == Variable::STACK) { + __ setlocal(var); + locals_->set(zone_, var->slot(), var->type()); + } else { + assert(var->location() == Variable::ENVIRONMENT); + setEnvSlot(var); + } + + __ pop(); +} + +void +BytecodeCompiler::visit(CallExpression *node) +{ +#if 0 + Local funType(zone_, node->callee()->type()); + + // Sniff out natives, which have a specific opcode and are guaranteed to + // be called via name proxy. + NamedFunction *native = NULL; + if (funType->isNative()) + native = node->callee()->toNameProxy()->binding()->toNamedFunction(); + + Local given(zone_); + for (size_t i = node->arguments()->length() - 1; i < node->arguments()->length(); i--) { + Expression *arg = node->arguments()->at(i); + + given = arg->type(); + + // We need to compute a reference if the parameter was in the form: + // f(&a) + // Note this form does not allow: f({}) + // + // We also need to compute references for all variadic native + // arguments, but arrays and references are already in the + // appropriate form. + bool mustPushReference = false; + if (i >= funType->parameters()->length()) { + assert(funType->isNative() && funType->isNativeVariadic()); + mustPushReference = !given->isArray() && !given->isReference(); + } else if (funType->parameterAt(i)->isReference()) { + mustPushReference = !given->isReference(); + } + + // If a native is passed to a native, it must only be one level deep. + assert(!funType->isNative() || !given->isArray() || given->levels() == 1); + + if (mustPushReference) { + if (arg->isNameProxy()) { + // :TODO: test with functions and globals + // Compute a reference to the name. + Variable *var = arg->toNameProxy()->binding()->toVariable(); + __ note_position(node->pos()); + __ newref(var); + } else if (arg->isFieldExpression()) { + FieldExpression *expr = arg->toFieldExpression(); + expr->left()->accept(this); + __ note_position(expr->pos()); + __ fieldref_lifo(expr->fieldIndex()); + } else { + // We don't know how to, or can't, build a direct reference to + // this type. Make an indirect one, by taking the top of the + // stack and boxing it up. + arg->accept(this); + __ note_position(node->pos()); + __ slotref_lifo(); + } + } else { + arg->accept(this); + } + } + + unsigned argc = node->arguments()->length(); + + // If there are missing arguments, then we're not done and have to push + // default parameters. + Local box(zone_); + Local array(zone_); + Local map(zone_); + for (size_t i = node->arguments()->length(); i < funType->parameters()->length(); i++) { + box = Box::cast(funType->defaults()->at(i)); + + if (box->type()->isPod()) { + if (box->type()->pod() == PrimitiveType_Int32) + __ int_(box->toInt32()); + else if (box->type()->pod() == PrimitiveType_Float) + __ float_(box->toFloat()); + else + assert(false); + + if (funType->parameterAt(i)->isReference()) { + __ note_position(node->pos()); + __ slotref_lifo(); + } + } else { + assert(box->type()->isArray()); + + array = Array::cast(box->toObject()); + if (funType->parameterAt(i)->isConst()) { + __ object(array); + } else { + // We need to copy the array since it's mutable. Note that + // although COPYARRAY returns its left-hand side, this may + // change in the future, we do the same thing and dupe the + // newly allocated array. + map = ArrayMap::cast(array->map()); + + // Natives cannot escape array references, so we can allocate + // them on the LIFO stack. + if (funType->isNative()) + __ newarray(OP_NEWFIXED, map, Heap::Tenure_Stack); + else + __ newarray(OP_NEWFIXED, map, Heap::Tenure_Default); + __ dup(); + __ object(array); + __ copyarray(); + __ pop(); + } + } + + argc++; + } + + __ note_position(node->pos()); + if (native) { + __ callnative(native->slot(), argc); + __ drop(argc); + } else { + node->callee()->accept(this); + __ call(funType); + __ drop(argc + 1); + } +#endif +} + +void +BytecodeCompiler::visit(TernaryExpression *node) +{ + Label success, failure, join; + + visitForTest(node->condition(), &success, &failure, &success); + __ bind(&success); + node->left()->accept(this); + __ drop(1); + __ jump(&join); + __ bind(&failure); + node->right()->accept(this); + __ bind(&join); +} + +void +BytecodeCompiler::visit(IfStatement *node) +{ + Label join; + if (!node->ifFalse()) { + Label success; + + visitForTest(node->condition(), &success, &join, &success); + __ bind(&success); + node->ifTrue()->accept(this); + } else { + Label success, next; + + visitForTest(node->condition(), &success, &next, &success); + __ bind(&success); + node->ifTrue()->accept(this); + __ jump(&join); + __ bind(&next); + node->ifFalse()->accept(this); + } + + __ bind(&join); +} + +bool +BytecodeCompiler::tableswitch(SwitchStatement *node) +{ + CaseValueList *values = node->caseValueList(); + + if (values->length() < 3) + return false; + + // Find the delta between the largest and smallest value. + int hi = values->at(values->length() - 1).box.toInt(); + int lo = values->at(0).box.toInt(); + assert(hi > lo); + + unsigned tablesize = TableSwitchEntries(lo, hi); + if (tablesize > MAX_TABLESWITCH_ENTRIES) + return false; + + Label def, join; + + // Create the list of labels that each statement will bind to. + Label *labels = new Label[node->cases()->length()]; + + // Create the table of labels we'll use for each case. + Label **table = new Label *[tablesize]; + + // Link all cases, by default, to the default label. + for (size_t i = 0; i < tablesize; i++) + table[i] = &def; + + // Now patch actual cases to their correct labels. + for (size_t i = 0; i < values->length(); i++) { + CaseValue &v = values->at(i); + assert(v.statement < node->cases()->length()); + + unsigned index = unsigned(v.box.toInt() - lo); + assert(index < tablesize); + + table[index] = &labels[v.statement]; + } + + // Emit the tableswitch opcode and the jump table. + __ tableswitch(lo, hi, &def, table); + + // We're done with the jump table now. + delete [] table; + + // Emit each statement. + for (size_t i = 0; i < node->cases()->length(); i++) { + Statement *stmt = node->cases()->at(i)->statement(); + + __ bind(&labels[i]); + stmt->accept(this); + __ jump(&join); + } + + // We're done with the statement labels. + delete [] labels; + + // Emit the default case. + __ bind(&def); + if (node->defaultCase()) + node->defaultCase()->accept(this); + + __ bind(&join); + + return true; +} + +void +BytecodeCompiler::visit(SwitchStatement *node) +{ + node->expression()->accept(this); + + if (tableswitch(node)) + return; + + Label join; + + for (size_t i = 0; i < node->cases()->length(); i++) { + Case *c = node->cases()->at(i); + + Label success, fail; + if (c->others()) { + for (size_t j = 0; j < c->others()->length(); j++) { + Expression *expr = c->others()->at(j); + + __ dup(); + expr->accept(this); + __ j(OP_JEQ, &success); + } + } + + // The final test. + __ dup(); + c->expression()->accept(this); + __ j(OP_JNE, &fail); + __ bind(&success); + c->statement()->accept(this); + __ jump(&join); + __ bind(&fail); + } + + if (node->defaultCase()) + node->defaultCase()->accept(this); + + __ pop(); + __ bind(&join); +} + +void +BytecodeCompiler::visit(FunctionStatement *node) +{ + if (!node->fun()->used()) + return; + + if (node->fun()->isNative()) { + assert(!moduleData_->natives->at(node->fun()->slot())); + + Local type(zone_, FunctionType::cast(node->fun()->type())); + Local name(zone_, node->fun()->name()); + Local native(zone_, Native::New(zone_, type, name)); + if (!native) + return; + + moduleData_->natives->set(zone_, node->fun()->slot(), native); + return; + } + + // If the function has no body, then it's just a declaration. + if (!node->body()) { + assert(node->fun()->isForward() || node->fun()->isPublic()); + return; + } + + BytecodeCompiler child(zone_, tree_, moduleData_); + Local fun(zone_, child.compileFunction(moduleData_, node)); + if (!fun) + return; + + assert(!moduleData_->module->getFunction(node->fun()->slot())); + moduleData_->module->setFunction(zone_, node->fun()->slot(), fun); +} + +void +BytecodeCompiler::visit(FunctionTypeStatement *node) +{ +} + +void +BytecodeCompiler::visit(EnumStatement *node) +{ +} + +void +BytecodeCompiler::visit(StructureStatement *node) +{ +} + +void +BytecodeCompiler::visit(ArrayLiteral *node) +{ + // We shouldn't reach these; they're handled manually when initializing + // declared arrays. + assert(false); +} + +void +BytecodeCompiler::visit(ImportStatement *node) +{ +} + +Code * +BytecodeCompiler::compileCode(Scope *scope, unsigned lineno) +{ + // STOP doubles as RETURNVOID. + __ stop(); + + if (__ oom() || zone_->hasPendingException()) + return NULL; + + Local bytes(zone_, ByteArray::New(zone_, __ length() * sizeof(bytecode), Heap::Tenure_Old)); + if (!bytes) + return NULL; + memcpy(bytes->raw(), __ buffer(), __ length()); + + for (size_t i = 0; i < scope->maxContainedLocals(); i++) + assert(locals_->at(i)); + + Local objects(zone_, FixedArray::New(zone_, __ objects()->length(), Heap::Tenure_Old)); + if (!objects) + return NULL; + + for (size_t i = 0; i < __ objects()->length(); i++) + objects->set(zone_, i, *(__ objects()->at(i))); + + Local safepoints(zone_); + + // If any stack slots hold gc pointers at potential failure points in the + // bytecode, record them now. We do this by building a list of tuples; + // each tuple contains the pc offset, and a bitset containing which stack + // slots must be scanned by the gc. + if (__ oopRecords()->length()) { + size_t bytesPerSafepoint = Code::BytesPerSafepoint(__ maxStackDepth()); + size_t gcmapSize = bytesPerSafepoint * __ oopRecords()->length(); + safepoints = ByteArray::New(zone_, gcmapSize, Heap::Tenure_Old); + if (!safepoints) + return NULL; + + Address cursor = safepoints->raw(); + for (size_t i = 0; i < __ oopRecords()->length(); i++) { + OopRecord *record = __ oopRecords()->at(i); + assert(record->depth <= __ maxStackDepth()); + + *reinterpret_cast(cursor) = record->offset; + + unsigned char *bits = reinterpret_cast(cursor + sizeof(unsigned)); + for (unsigned i = 0; i < record->depth; i++) + bits[i / 8] |= (1 << (i % 8)); + + cursor += bytesPerSafepoint; + } + + assert(cursor == safepoints->raw() + gcmapSize); + } + + Local sourcemap(zone_); + if (__ sourcemapLength()) { + sourcemap = ByteArray::New(zone_, __ sourcemapLength(), Heap::Tenure_Old); + if (!sourcemap) + return NULL; + + memcpy(sourcemap->raw(), __ sourcemap(), __ sourcemapLength()); + } + + Local code(zone_); + + code = Code::New(zone_, + bytes, + locals_, + objects, + __ maxStackDepth(), + moduleData_->module, + safepoints, + sourcemap, + lineno); + return code; +} + +Function * +BytecodeCompiler::compileFunction(ModuleData *module, FunctionStatement *node) +{ + // Note: No need to record a life slot here, since it's part of the Frame. + AutoEnterScope functionScope(this, node->scope()); + + // Set the initial line, because line mappings are deltas. + __ setFirstLine(node->pos().line); + locals_ = FixedArray::New(zone_, node->scope()->maxContainedLocals(), Heap::Tenure_Old); + + node->body()->accept(this); + + unsigned nargs = node->scope()->numArguments(); + Local args(zone_, FixedArray::New(zone_, nargs, Heap::Tenure_Old)); + if (!args) + return NULL; + + for (size_t i = 0; i < node->scope()->numArguments(); i++) + args->set(zone_, i, node->scope()->getArgument(i)->type()); + + + Local code(zone_, compileCode(node->scope(), node->pos().line)); + if (!code) + return NULL; + + Local type(zone_, FunctionType::cast(node->fun()->type())); + Local name(zone_, node->fun()->name()); + Local fun(zone_, Function::New(zone_, code, type, name, node->pos())); + if (!fun) + return NULL; + + // If this function had a forward, export it as a named public. + if (node->fun()->isPublic()) { + assert(node->fun()->isPublic()); + ScopedRoot root(fun); + if (!root) + return NULL; + if (!moduleData_->publics.append(root)) + return NULL; + } + +#ifndef NDEBUG + Disassemble(code, stdout); +#endif + + return fun; +} + +Module * +BytecodeCompiler::compile() +{ + AutoEnterScope scope(this, tree_->globalScope()); + + ModuleData moduleData; + + moduleData_ = &moduleData; + moduleData_->module = Module::New(zone_, tree_->globalScope()->numFunctions()); + + if (!moduleData_) + return NULL; + + moduleData_->natives = FixedArray::New(zone_, tree_->globalScope()->numNatives(), Heap::Tenure_Old); + if (!moduleData_->natives) + return NULL; + + for (size_t i = 0; i < tree_->statements()->length(); i++) { + Statement *statement = tree_->statements()->at(i); + statement->accept(this); + } + + if (zone_->hasPendingException()) + return NULL; + +#ifndef NDEBUG + for (size_t i = 0; i < tree_->globalScope()->numFunctions(); i++) + assert(moduleData_->module->getFunction(i)); + for (size_t i = 0; i < tree_->globalScope()->numNatives(); i++) + assert(moduleData_->natives->at(i)); +#endif + + if (moduleData_->publics.length()) { + Local fun(zone_); + Local list(zone_, FixedArray::New(zone_, moduleData_->publics.length(), + Heap::Tenure_Old)); + if (!list) + return NULL; + + for (size_t i = 0; i < moduleData_->publics.length(); i++) + list->set(zone_, i, moduleData_->publics[i]); + + moduleData_->module->setPublics(list); + } + + moduleData_->module->setNatives(moduleData_->natives); + + if (scope_->numEnvironmentVars()) { + Local globalMap(zone_, scope_->createEnvironmentMap(zone_)); + if (!globalMap) + return NULL; + + Local environment(zone_, Environment::New(zone_, globalMap)); + if (!environment) + return NULL; + + moduleData_->module->setEnvironment(environment); + } + + Local code(zone_, compileCode(tree_->globalScope(), 1)); + if (!code) + return NULL; + + Disassemble(code, stdout); + + moduleData_->module->setCode(code); + + return moduleData_->module; +} diff -r c4b1341297e5 -r ba85a47ee414 src/OldByCo.h --- a/src/OldByCo.h Mon Jun 10 00:46:29 2013 +0100 +++ b/src/OldByCo.h Mon Jun 10 01:04:43 2013 +0100 @@ -1,160 +1,160 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of JITCraft. - * - * JITCraft is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * JITCraft. If not, see http://www.gnu.org/licenses/. - */ -#ifndef _include_bytecode_compiler_h_ -#define _include_bytecode_compiler_h_ - -#include "../Functions.h" -#include "AST.h" -#include "BytecodeEmitter.h" - -namespace ke { - -class Module; -class Array; -class ArrayMap; - -struct ModuleData -{ - ScopedRoot module; - PoolList > publics; - ScopedRoot natives; -}; - -class BytecodeCompiler : public AstVisitor -{ - Zone *zone_; - ParseTree *tree_; - BytecodeEmitter emitter_; - ScopedRoot locals_; - Scope *scope_; - ModuleData *moduleData_; - - class LoopScope - { - Scope *scope_; - BytecodeEmitter::Label *bk_; - BytecodeEmitter::Label *ct_; - LoopScope **prevp_; - LoopScope *prev_; - - public: - LoopScope(Scope *scope, BytecodeEmitter::Label *bk, BytecodeEmitter::Label *ct, - LoopScope **prevp) - : scope_(scope), - bk_(bk), - ct_(ct), - prevp_(prevp), - prev_(*prevp) - { - *prevp_ = this; - } - - ~LoopScope() - { - *prevp_ = prev_; - } - - BytecodeEmitter::Label *break_() { - return bk_; - } - BytecodeEmitter::Label *continue_() { - return ct_; - } - Scope *scope() { - return scope_; - } - }; - - LoopScope *loop_; - - public: - class AutoEnterScope - { - BytecodeCompiler *bc_; - Scope *saved_; - - public: - AutoEnterScope(BytecodeCompiler *bc, Scope *scope) - : bc_(bc), - saved_(bc->scope_) - { - if (scope) - bc->scope_ = scope; - } - ~AutoEnterScope() { - bc_->scope_ = saved_; - } - }; - - private: - void recordLifoSlot(Scope *scope); - void unwindTo(Scope *scope); - void visitForTest(Expression *expression, - BytecodeEmitter::Label *trueBranch, - BytecodeEmitter::Label *falseBranch, - BytecodeEmitter::Label *fallthrough); - Code *compileCode(Scope *scope, unsigned lineno); - Function *compileFunction(ModuleData *module, FunctionStatement *fun); - void visitForALU(Type *needed, Expression *node); - - void getEnvSlot(Variable *var); - void setEnvSlot(Variable *var); - - bool tableswitch(SwitchStatement *node); - - public: - BytecodeCompiler(Zone *zone, ParseTree *tree, ModuleData *module); - - Module *compile(); - - public: - void visit(NameProxy *name); - void visit(Assignment *node); - void visit(BinaryExpression *node); - void visit(ReturnStatement *node); - void visit(ForStatement *node); - void visit(BlockStatement *node); - void visit(VariableDeclaration *node); - void visit(IntegerLiteral *node); - void visit(BooleanLiteral *node); - void visit(FloatLiteral *node); - void visit(StringLiteral *node); - void visit(ExpressionStatement *node); - void visit(FunctionStatement *node); - void visit(CallExpression *node); - void visit(IfStatement *node); - void visit(IndexExpression *node); - void visit(EnumStatement *node); - void visit(WhileStatement *node); - void visit(BreakStatement *node); - void visit(ContinueStatement *node); - void visit(IncDecExpression *node); - void visit(UnaryExpression *node); - void visit(TernaryExpression *node); - void visit(StructureStatement *node); - void visit(FieldExpression *node); - void visit(SwitchStatement *node); - void visit(FunctionTypeStatement *node); - void visit(ArrayLiteral *node); - void visit(ImportStatement *node); -}; - -} - -#endif // _include_bytecode_generator_h_ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_bytecode_compiler_h_ +#define _include_bytecode_compiler_h_ + +#include "../Functions.h" +#include "AST.h" +#include "BytecodeEmitter.h" + +namespace ke { + +class Module; +class Array; +class ArrayMap; + +struct ModuleData +{ + ScopedRoot module; + PoolList > publics; + ScopedRoot natives; +}; + +class BytecodeCompiler : public AstVisitor +{ + Zone *zone_; + ParseTree *tree_; + BytecodeEmitter emitter_; + ScopedRoot locals_; + Scope *scope_; + ModuleData *moduleData_; + + class LoopScope + { + Scope *scope_; + BytecodeEmitter::Label *bk_; + BytecodeEmitter::Label *ct_; + LoopScope **prevp_; + LoopScope *prev_; + + public: + LoopScope(Scope *scope, BytecodeEmitter::Label *bk, BytecodeEmitter::Label *ct, + LoopScope **prevp) + : scope_(scope), + bk_(bk), + ct_(ct), + prevp_(prevp), + prev_(*prevp) + { + *prevp_ = this; + } + + ~LoopScope() + { + *prevp_ = prev_; + } + + BytecodeEmitter::Label *break_() { + return bk_; + } + BytecodeEmitter::Label *continue_() { + return ct_; + } + Scope *scope() { + return scope_; + } + }; + + LoopScope *loop_; + + public: + class AutoEnterScope + { + BytecodeCompiler *bc_; + Scope *saved_; + + public: + AutoEnterScope(BytecodeCompiler *bc, Scope *scope) + : bc_(bc), + saved_(bc->scope_) + { + if (scope) + bc->scope_ = scope; + } + ~AutoEnterScope() { + bc_->scope_ = saved_; + } + }; + + private: + void recordLifoSlot(Scope *scope); + void unwindTo(Scope *scope); + void visitForTest(Expression *expression, + BytecodeEmitter::Label *trueBranch, + BytecodeEmitter::Label *falseBranch, + BytecodeEmitter::Label *fallthrough); + Code *compileCode(Scope *scope, unsigned lineno); + Function *compileFunction(ModuleData *module, FunctionStatement *fun); + void visitForALU(Type *needed, Expression *node); + + void getEnvSlot(Variable *var); + void setEnvSlot(Variable *var); + + bool tableswitch(SwitchStatement *node); + + public: + BytecodeCompiler(Zone *zone, ParseTree *tree, ModuleData *module); + + Module *compile(); + + public: + void visit(NameProxy *name); + void visit(Assignment *node); + void visit(BinaryExpression *node); + void visit(ReturnStatement *node); + void visit(ForStatement *node); + void visit(BlockStatement *node); + void visit(VariableDeclaration *node); + void visit(IntegerLiteral *node); + void visit(BooleanLiteral *node); + void visit(FloatLiteral *node); + void visit(StringLiteral *node); + void visit(ExpressionStatement *node); + void visit(FunctionStatement *node); + void visit(CallExpression *node); + void visit(IfStatement *node); + void visit(IndexExpression *node); + void visit(EnumStatement *node); + void visit(WhileStatement *node); + void visit(BreakStatement *node); + void visit(ContinueStatement *node); + void visit(IncDecExpression *node); + void visit(UnaryExpression *node); + void visit(TernaryExpression *node); + void visit(StructureStatement *node); + void visit(FieldExpression *node); + void visit(SwitchStatement *node); + void visit(FunctionTypeStatement *node); + void visit(ArrayLiteral *node); + void visit(ImportStatement *node); +}; + +} + +#endif // _include_bytecode_generator_h_ diff -r c4b1341297e5 -r ba85a47ee414 src/OldSema.cpp --- a/src/OldSema.cpp Mon Jun 10 00:46:29 2013 +0100 +++ b/src/OldSema.cpp Mon Jun 10 01:04:43 2013 +0100 @@ -1,1899 +1,1899 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of JITCraft. - * - * JITCraft is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * JITCraft. If not, see http://www.gnu.org/licenses/. - */ -#include -#include "SemanticAnalysis.h" -#include "../Types.h" -#include "../Zone.h" -#include "../TypeManager.h" -#include "../Strings.h" -#include "../Box.h" -#include "../Array.h" -#include "../Heap-inl.h" -#include "Scopes.h" -#include "NameBinding.h" - -using namespace ke; - -SemanticAnalysis::SemanticAnalysis(Zone *zone, CompileContext &cc, ParseTree *tree) - : zone_(zone), - cc_(cc), - tree_(tree), - scope_(NULL), - loop_(NULL) -{ -} - -bool -SemanticAnalysis::analyze() -{ - assert(false); - return false; -} - - -#if 0 - -static inline bool -IsPrimitiveType(Type *type, PrimitiveType primitive) -{ - if (!type) - return false; - - if (!type->isPrimitive()) - return false; - - return primitive == type->primitive(); -} - -static inline bool -EvaluateBinaryOperation(TokenKind kind, const BoxedPrimitive &left, const BoxedPrimitive &right, - BoxedPrimitive *out) -{ - switch (kind) { - case TOK_PLUS: - *out = BoxedPrimitive::Int(left.toInt() + right.toInt()); - return true; - - case TOK_SHL: - *out = BoxedPrimitive::Int(left.toInt() << right.toInt()); - return true; - - default: - return false; - } -} - -static inline bool -EvaluateUnaryOperation(TokenKind kind, const BoxedPrimitive &inner, BoxedPrimitive *out) -{ - switch (kind) { - case TOK_MINUS: - *out = BoxedPrimitive::Int(-inner.toInt()); - return true; - - default: - return false; - } -} - -static bool -EvaluateForConstant(Expression *expression, BoxedPrimitive *out) -{ - switch (expression->kind()) { - case AstNode::kBinaryExpression: - { - BoxedPrimitive left; - BoxedPrimitive right; - BinaryExpression *b = expression->toBinaryExpression(); - if (!EvaluateForConstant(b->left(), &left) || - !EvaluateForConstant(b->right(), &right)) - { - return false; - } - return EvaluateBinaryOperation(b->token(), left, right, out); - } - - case AstNode::kUnaryExpression: - { - BoxedPrimitive inner; - UnaryExpression *u = expression->toUnaryExpression(); - if (!EvaluateForConstant(u->expression(), &inner)) - return false; - return EvaluateUnaryOperation(u->token(), inner, out); - } - - case AstNode::kNameProxy: - { - NameProxy *proxy = expression->toNameProxy(); - if (proxy->binding()->kind() != BoundName::CONSTANT) - return false; - - NamedConstant *c = proxy->binding()->toNamedConstant(); - if (!IsPrimitiveType(c->type(), PrimitiveType_Int32)) - return false; - - *out = c->value(); - return true; - } - - case AstNode::kBooleanLiteral: - *out = BoxedPrimitive::Int(expression->toBooleanLiteral()->token() == TOK_TRUE ? 1 : 0); - return true; - - case AstNode::kIntegerLiteral: - *out = BoxedPrimitive::Int(expression->toIntegerLiteral()->value()); - return true; - - default: - return false; - } -} - -SemanticAnalysis::SemanticAnalysis(Zone *zone, CompileContext &cc, ParseTree *tree) - : zone_(zone), - cc_(cc), - tree_(tree), - scope_(NULL), - loop_(NULL) -{ -} - -bool -SemanticAnalysis::analyze() -{ - if (cc_.failures()) - return false; - - scope_ = tree_->globalScope(); - assert(scope_->kind() == Scope::GLOBAL); - - for (size_t i = 0; i < tree_->statements()->length(); i++) { - Statement *statement = tree_->statements()->at(i); - if (statement->isFunctionStatement()) { - FunctionStatement *funstmt = statement->toFunctionStatement(); - if (!preanalyze(funstmt)) - return false; - } else if (statement->isEnumStatement()) { - EnumStatement *enumstmt = statement->toEnumStatement(); - - // We pre-fill global enum types, since they may occur in function - // signatures. This should probably be somewhere else... - enumstmt->accept(this); - } else if (statement->isStructureStatement()) { - StructureStatement *node = statement->toStructureStatement(); - node->accept(this); - if (!node->decl()->type()) - return false; - } else if (statement->isFunctionTypeStatement()) { - FunctionTypeStatement *node = statement->toFunctionTypeStatement(); - node->accept(this); - if (!node->decl()->type()) - return false; - } - } - - for (size_t i = 0; i < tree_->statements()->length(); i++) { - Statement *statement = tree_->statements()->at(i); - - // We can't double-process these. Maybe put this check somewhere else. - if (statement->isEnumStatement()) - continue; - - statement->accept(this); - } - - if (!determineLiveFunctions()) - return false; - - return !cc_.failures(); -} - -bool -SemanticAnalysis::determineLiveFunctions() -{ - Vector worklist; - - // Add the root set. - for (size_t i = 0; i < tree_->statements()->length(); i++) { - Statement *statement = tree_->statements()->at(i); - if (!statement->isFunctionStatement()) - continue; - - FunctionStatement *stmt = statement->toFunctionStatement(); - NamedFunction *fun = stmt->fun(); - if (!fun->used()) - continue; - - for (size_t i = 0; i < fun->usedGlobals()->length(); i++) { - NamedFunction *name = fun->usedGlobals()->at(i)->toNamedFunction(); - - // Names in these lists are always unused, however, we mark them - // as used while inserting into the work queue, to avoid double- - // insertion. - if (name->used()) - continue; - - if (!worklist.append(name)) - return false; - - name->setUsed(); - } - } - - while (!worklist.empty()) { - NamedFunction *fun = worklist.popCopy(); - - for (size_t i = 0; i < fun->usedGlobals()->length(); i++) { - NamedFunction *name = fun->usedGlobals()->at(i)->toNamedFunction(); - if (name->used()) - continue; - if (!worklist.append(name)) - return false; - name->setUsed(); - } - } - - return true; -} - -Type * -SemanticAnalysis::bindType(const SourcePosition &pos, BoundName *name) -{ - if (!name) - return zone_->types()->getPrimitive(PrimitiveType_Int32); - - if (name->kind() == BoundName::UNBOUND) { - cc_.reportError(pos, Message_TypeNotFound, name->name()->chars()); - return NULL; - } - - if (name->kind() != BoundName::TYPE) { - cc_.reportError(pos, Message_IdentifierIsNotAType, name->name()->chars()); - return NULL; - } - - if (!name->type()) - return NULL; - - return name->type(); -} - -Type * -SemanticAnalysis::visitAndApplyType(Expression *expression) -{ - expression->accept(this); - return expression->type(); -} - -Type * -SemanticAnalysis::visitForLoad(Expression *expression) -{ - Local type(zone_, visitAndApplyType(expression)); - if (!type) - return NULL; - - if (type->isReference()) { - expression->setType(type->contained()); - } else if (expression->isCallExpression()) { - if (type->isVoid()) { - CallExpression *call = expression->toCallExpression(); - cc_.reportError(call->callee()->pos(), Message_FunctionDoesNotReturnValue); - return NULL; - } - } - - return expression->type(); -} - -void -SemanticAnalysis::unaryTypeError(AstNode *node, TokenKind token, Handle type) -{ - const char *op = TokenNames[token]; - const char *name = GetTypeName(type); - - cc_.reportError(node->pos(), Message_UnaryTypeError, op, name); -} - -void -SemanticAnalysis::binaryTypeError(AstNode *node, TokenKind token, Handle left, Handle right) -{ - const char *op = TokenNames[token]; - const char *leftName = GetTypeName(left); - const char *rightName = GetTypeName(right); - - cc_.reportError(node->pos(), Message_BinaryTypeError, op, leftName, rightName); -} - -Type * -SemanticAnalysis::typeCheckBinary(AstNode *node, TokenKind token, Handle left, Handle right) -{ - if ((token >= TOK_PLUS && token <= TOK_SLASH) || - (token >= TOK_ASSIGN_ADD && token <= TOK_ASSIGN_DIV)) - { - // If either side is a float, the other side must be a float or int. - if (left->isFloat() || right->isFloat()) { - Type *floatType = left->isFloat() ? left : right; - Type *other = left->isFloat() ? right : left; - if (other->isFloat() || other->isInt32()) - return floatType; - } - } - - if (token == TOK_OR || token == TOK_AND) { - if (!canTest(node, left) || !canTest(node, right)) - return NULL; - return zone_->types()->getPrimitive(PrimitiveType_Bool); - } - - if (token >= TOK_EQUALS && token <= TOK_GE) { - if ((left->isFloat() || left->isInt32()) && - (right->isFloat() || right->isInt32())) - { - return zone_->types()->getPrimitive(PrimitiveType_Bool); - } - binaryTypeError(node, token, left, right); - return NULL; - } - - if (!left->isInt32() || !right->isInt32()) { - binaryTypeError(node, token, left, right); - return NULL; - } - - return left; -} - -bool -SemanticAnalysis::canTest(AstNode *node, Handle type) -{ - if (!type->isPrimitive() && !type->isEnum()) { - cc_.reportError(node->pos(), Message_ArrayIsNotBoolean); - return false; - } - return true; -} - -Type * -SemanticAnalysis::visitForLValue(Expression *expression) -{ - if (expression->isIndexExpression()) { - Type *type = visitAndApplyType(expression); - if (!type) - return NULL; - - // if (type->isConst()) { - // cc_.reportError(expression->pos(), Message_ReferenceIsConstant); - // return NULL; - // } - - return type; - } - - if (expression->isFieldExpression()) { - Type *type = visitAndApplyType(expression); - if (!type) - return NULL; - - // if (expression->toFieldExpression()->left()->type()->isConst()) { - // cc_.reportError(expression->pos(), Message_ReferenceIsConstant); - // return NULL; - // } - - return type; - } - - if (!expression->isNameProxy()) { - cc_.reportError(expression->pos(), Message_ExpectedLValue); - return NULL; - } - - NameProxy *proxy = expression->toNameProxy(); - if (!proxy->binding()->kind() == BoundName::VARIABLE) { - cc_.reportError(expression->pos(), Message_ExpectedLValue); - return NULL; - } - - Variable *var = proxy->binding()->toVariable(); - if (!var->type()) - return NULL; - - if (var->type()->isReference()) { - if (var->type()->isConst()) { - cc_.reportError(expression->pos(), Message_ReferenceIsConstant); - return NULL; - } - return var->type()->contained(); - } - return var->type(); -} - -SemanticAnalysis::ControlStatus -SemanticAnalysis::visitForControl(Statement *statement) -{ - ControlScope scope(&control_); - statement->accept(this); - return scope.status(); -} - -void -SemanticAnalysis::visit(NameProxy *name) -{ - BoundName *binding = name->binding(); - - if (binding->scope()->kind() == Scope::GLOBAL) { - if (!function_ || function_->isPublic()) { - // Whatever we're referencing must be kept alive no matter what. - // As such, we simply mark it used, and don't do anything special. - binding->setUsed(); - } else if (!binding->used() && binding->kind() == BoundName::FUNCTION) { - // Otherwise, we're inside a normal stock or function. Since we - // don't really know if we've been used yet, we shouldn't - // prematurely references as alive. Instead, we keep a list of - // which names have been used, so we can mark them later if needed. - // - // Note that we skip this step if the binding is already in the - // root set. - if (!function_->addUsedGlobal(binding)) - return; - } - } - - name->setType(binding->type()); -} - -void -SemanticAnalysis::visit(Assignment *node) -{ - Local exprType(zone_, visitForLoad(node->expression())); - if (!exprType) - return ; - - Local type(zone_, visitForLValue(node->lvalue())); - if (!type) - return; - - if (node->token() != TOK_ASSIGN) { - if (!typeCheckBinary(node, node->token(), type, exprType)) - return; - } - - if (!typeCheckAssignment(node->pos(), exprType, type, true)) - return; - - node->setType(type); -} - -void -SemanticAnalysis::visit(IncDecExpression *node) -{ - Local type(zone_, visitForLValue(node->expression())); - if (!type) - return; - - assert(!type->isReference()); - - if (!type->isInt32() && !type->isFloat()) { - unaryTypeError(node, node->token(), type); - return; - } - - node->setType(type); -} - -void -SemanticAnalysis::visit(UnaryExpression *node) -{ - Local type(zone_, visitForLoad(node->expression())); - if (!type) - return; - - switch (node->token()) - { - case TOK_NOT: - if (!canTest(node->expression(), type)) - return; - type = zone_->types()->getPrimitive(PrimitiveType_Int32); - if (!type) - return; - node->setType(type); - break; - - case TOK_MINUS: - if (!type->isFloat() && !type->isInt32()) { - unaryTypeError(node, node->token(), type); - return; - } - node->setType(type); - break; - - case TOK_LABEL: - { - Local to(zone_, bindType(node->pos(), node->type())); - if (!to) - return; - - if ((!type->isFloat() && !type->isInt32OrEnum()) || - (!to->isFloat() && !to->isInt32OrEnum())) - { - unaryTypeError(node, node->token(), type); - return; - } - - node->setType(to); - break; - } - - case TOK_SIZEOF: - { - if (!type->isArray()) { - cc_.reportError(node->pos(), Message_SizeofNeedsArray); - return; - } - node->setType(zone_->types()->getPrimitive(PrimitiveType_Int32)); - break; - } - - default: - assert(node->token() == TOK_TILDE); - if (!type->isInt32()) { - unaryTypeError(node, node->token(), type); - return; - } - node->setType(type); - break; - } -} - -void -SemanticAnalysis::visit(TernaryExpression *node) -{ - Local type(zone_, visitForLoad(node->condition())); - if (!type) - return; - - if (!canTest(node->condition(), type)) - return; - - Local left(zone_, visitForLoad(node->left())); - Local right(zone_, visitForLoad(node->right())); - if (!left || !right) - return; - - if (left != right) { - binaryTypeError(node, TOK_QMARK, left, right); - return; - } -} - -void -SemanticAnalysis::visit(BinaryExpression *node) -{ - Local left(zone_, visitForLoad(node->left())); - Local right(zone_, visitForLoad(node->right())); - if (!left || !right) - return; - - Local result(zone_, typeCheckBinary(node, node->token(), left, right)); - if (!result) - return; - - node->setType(result); -} - -void -SemanticAnalysis::visit(ReturnStatement *node) -{ - // Set this early, so if we error we don't get more errors. - control_->returns(Returns_Always); - - if (!node->expression()) { - if (!function_->type()->returnType()->isVoid()) - cc_.reportError(node->pos(), Message_NotAllPathsReturnValue); - return; - } - - Local type(zone_, visitForLoad(node->expression())); - if (!type) - return; - - Local returnType(zone_, function_->type()->returnType()); - if (!typeCheckAssignment(node->pos(), type, returnType, true)) - return; -} - -void -SemanticAnalysis::visit(ForStatement *node) -{ - LoopScope loop(&loop_); - - if (node->scope()) - scope_ = node->scope(); - - if (node->initialization()) - node->initialization()->accept(this); - - if (node->condition()) { - Local type(zone_, visitForLoad(node->condition())); - if (!type || !canTest(node->condition(), type)) { - return; - } - } - - if (node->update()) - node->update()->accept(this); - - ControlStatus status = visitForControl(node->body()); - - // If there is a condition, we treat this like a while loop. Otherwise, - // join point is only reachable if the loop contains a break. The - // termination status doesn't tell us anything yet. - if (node->condition() || loop.breaks()) - control_->merge(status, ControlStatus(false, Returns_Never)); - else - control_->terminate(status.returns()); - - if (node->scope()) - scope_ = scope_->enclosing(); -} - -void -SemanticAnalysis::visit(WhileStatement *node) -{ - LoopScope loop(&loop_); - - Local type(zone_, visitForLoad(node->condition())); - if (!type || !canTest(node->condition(), type)) { - return; - } - - ControlStatus status = visitForControl(node->body()); - - // Detect loops that will always have a fork in the control-flow graph. - // This will be a |while| loop, or a |do-while| loop whose body does - // not terminate. - // - // For now, we treat these as an |if| regardless of the direction the - // branch can take. That means the control-flow is always conditional. - if (node->token() == TOK_WHILE || - !status.terminates() || - loop.breaks()) - { - control_->merge(status, ControlStatus(false, Returns_Never)); - } else { - // At this point, we've got a do-while loop with a |return| or - // |continue|, meaning that the rest of the block is unreachable. - control_->terminate(status.returns()); - } -} - -void -SemanticAnalysis::visit(BlockStatement *node) -{ - if (node->scope()) - scope_ = node->scope(); - for (size_t i = 0; i < node->statements()->length(); i++) { - Statement *statement = node->statements()->at(i); - statement->accept(this); - } - if (node->scope()) - scope_ = scope_->enclosing(); -} - -static inline bool -IsScalarString(Type *type) -{ - if (type->isPrimitive() && type->primitive() == PrimitiveType_Char) - return true; - if (type->isReference()) - return IsScalarString(type->contained()); - return false; -} - -static const int INFER_ARRAY_SIZE = 0; -static const int EVAL_ARRAY_SIZE = -1; -static const int DYNAMIC_ARRAY_SIZE = -2; - -bool -SemanticAnalysis::evaluateDimensions(ExpressionList *exprs, int dims[MAX_ARRAY_DEPTH]) -{ - bool hadEmptySize = false; - - for (size_t i = 0; i < exprs->length(); i++) { - Expression *expr = exprs->at(i); - if (!expr) { - dims[i] = INFER_ARRAY_SIZE; - hadEmptySize = true; - continue; - } - - BoxedPrimitive box; - if (EvaluateForConstant(expr, &box) && box.type() == PrimitiveType_Int32) { - int size = box.toInt(); - if (size <= 0) { - cc_.reportError(expr->pos(), Message_BadArraySize); - return false; - } - - dims[i] = size; - continue; - } - - Local exprType(zone_, visitForLoad(expr)); - if (!exprType) - return false; - - if (!IsPrimitiveType(exprType, PrimitiveType_Int32)) { - cc_.reportError(expr->pos(), Message_ArrayDimensionNotInteger); - return false; - } - - if (hadEmptySize) { - cc_.reportError(expr->pos(), Message_BadDynamicInitializer); - return false; - } - - dims[i] = EVAL_ARRAY_SIZE; - } - - return true; -} - -bool -SemanticAnalysis::inferFixedArrayLiteral(ArrayLiteral *literal, Handle contained, - int dims[MAX_ARRAY_DEPTH], - unsigned dimcount, unsigned level) -{ - if (dims[level] == EVAL_ARRAY_SIZE) { - cc_.reportError(literal->pos(), Message_CannotInitializeEvalArray); - return false; - } - - if (dims[level] == INFER_ARRAY_SIZE) { - // Strings always become dynamic arrays. - if (level != dimcount - 1 || !IsPrimitiveType(contained, PrimitiveType_Char)) - dims[level] = literal->expressions()->length(); - } else if (dims[level] == DYNAMIC_ARRAY_SIZE) { - cc_.reportError(literal->pos(), Message_CannotInitializeEvalArray); - return false; - } else { - if (literal->expressions()->length() != unsigned(dims[level])) { - cc_.reportError(literal->pos(), Message_ArrayDimensionSizeMismatch, level + 1); - return false; - } - } - - if (level == dimcount - 2 && IsPrimitiveType(contained, PrimitiveType_Char)) { - for (size_t i = 0; i < literal->expressions()->length(); i++) { - Expression *expr = literal->expressions()->at(i); - if (!expr->isStringLiteral()) { - cc_.reportError(expr->pos(), Message_ExpectedStringLiteral); - return false; - } - - StringLiteral *lit = expr->toStringLiteral(); - if (dims[level + 1] > 0 && - unsigned(dims[level]) < lit->string()->length()) - { - cc_.reportError(lit->pos(), Message_ArrayDimensionSizeMismatch, level + 2); - return false; - } - if (!visitAndApplyType(lit)) - return false; - } - } else if (level == dimcount - 1) { - Local type(zone_); - for (size_t i = 0; i < literal->expressions()->length(); i++) { - Expression *expr = literal->expressions()->at(i); - if (expr->isArrayLiteral()) { - cc_.reportError(expr->pos(), Message_ArrayDimensionsMismatch); - return false; - } - - if ((type = visitAndApplyType(expr)) == NULL) - return false; - - if (!typeCheckAssignment(expr->pos(), type, contained, true)) - return false; - } - } else { - for (size_t i = 0; i < literal->expressions()->length(); i++) { - Expression *expr = literal->expressions()->at(i); - if (!expr->isArrayLiteral()) { - cc_.reportError(expr->pos(), Message_ArrayDimensionsMismatch); - return false; - } - - assert(expr->toArrayLiteral()->isFixed()); - - if (!inferFixedArrayLiteral(expr->toArrayLiteral(), - contained, - dims, - dimcount, - level + 1)) - { - return false; - } - } - } - - return true; -} - -void -SemanticAnalysis::visit(VariableDeclaration *node) -{ - Local type(zone_, bindType(node->pos(), node->type())); - if (!type) - return; - -#if 0 - if (node->postDimensions() || type->isArray()) { - // The type could be an array type, so sum the type and postDimensions - // together. - unsigned dimcount = node->postDimensions()->length(); - if (type->isArray()) - dimcount += type->levels(); - if (dimcount > MAX_ARRAY_DEPTH) { - cc_.reportError(node->pos(), Message_MaximumArrayDepth); - return; - } - - // Take an initial guess at the array dimension sizes. - int dims[MAX_ARRAY_DEPTH]; - if (!evaluateDimensions(node->postDimensions(), dims)) - return; - - // Now add in the type's array dimensions. - Local contained(zone_, type); - for (size_t i = node->postDimensions()->length(); i < dimcount; i++) { - if (type->isFixedLength()) - dims[i] = type->fixedLength(); - else - dims[i] = DYNAMIC_ARRAY_SIZE; - contained = contained->contained(); - } - - Expression *initializer = node->initialization(); - if (initializer && - initializer->isArrayLiteral() && - initializer->toArrayLiteral()->isFixed()) - { - // Fixed array initializers are special; we use them to infer any - // missing information in the original array. We do not evaluate - // the array initializer as an expression. - if (!inferFixedArrayLiteral(initializer->toArrayLiteral(), - contained, - dims, - dimcount, - 0)) - { - return; - } - initializer = NULL; - } - - // We now have enough information to build the actual array type. - // Start from the original dimcount, not the one computed for - // initializers. - for (size_t i = node->postDimensions()->length() - 1; - i < node->postDimensions()->length(); - i--) - { - int size = dims[i] > 0 - ? dims[i] - : Type::DYNAMIC_ARRAY_SIZE; - - type = zone_->types()->newArray(type, size, false); - if (!type) - return; - } - - // Finally, if we had a non-fixed initializer, make sure it type - // checks. - if (initializer) { - Local right(zone_, visitForLoad(initializer)); - if (!right) - return; - - if (!typeCheckAssignment(node->pos(), right, type, true)) - return; - } - } else { - if (node->initialization()) { - Local right(zone_, visitForLoad(node->initialization())); - if (!right) - return; - if (!typeCheckAssignment(node->pos(), right, type, true)) - return; - } - - if (type->isPrimitive() && type->primitive() == PrimitiveType_Char) { - cc_.reportError(node->pos(), Message_CannotDeclareStringCell); - return; - } - } -#endif - -#if 0 - // If this variable is stack allocated, we could use the LIFO heap - // to allocate it. If that's the case, make sure to reserve a slot - // so we can unwind the LIFO heap at the end of this scope. - if (!node->variable()->needsHeapAllocation()) { - if (type->isArray() || type->isStruct()) - scope_->setUsesLifoHeap(); - } - - node->variable()->setType(type); -#endif -} - -void -SemanticAnalysis::visit(IntegerLiteral *node) -{ - node->setType(zone_->types()->getPrimitive(PrimitiveType_Int32)); -} - -void -SemanticAnalysis::visit(BooleanLiteral *node) -{ - node->setType(zone_->types()->getPrimitive(PrimitiveType_Bool)); -} - -void -SemanticAnalysis::visit(ExpressionStatement *node) -{ - visitAndApplyType(node->expression()); -} - -Box * -SemanticAnalysis::evaluateDefaultParameter(Handle type, Parameter *parameter) -{ - Local given(zone_, visitForLoad(parameter->initializer())); - - BoxedPrimitive prim; - if (EvaluateForConstant(parameter->initializer(), &prim)) { - if (!typeCheckAssignment(parameter->initializer()->pos(), given, type, false)) - return NULL; - - if (prim.type() == PrimitiveType_Int32) - return Box::New(zone_, given, prim.toInt()); - return Box::New(zone_, given, prim.toFloat()); - } else if (parameter->initializer()->isStringLiteral()) { - StringLiteral *literal = parameter->initializer()->toStringLiteral(); - - if (!typeCheckAssignment(parameter->initializer()->pos(), given, type, false)) - return NULL; - - Local map(zone_, ArrayMap::Attach(zone_, given)); - if (!map) - return NULL; - - Local array(zone_, - Array::NewString(zone_, - map, - literal->string()->raw(), - literal->string()->length(), - Heap::Tenure_Old)); - if (!array) - return NULL; - - return Box::NewObj(zone_, given, array, Heap::Tenure_Old); - } - - cc_.reportError(parameter->initializer()->pos(), Message_MustBeConstant); - return NULL; -} - -bool -SemanticAnalysis::buildFunctionArgs(AstNode *node, TokenKind kind, const FunctionSignature &sig, - FixedArray **argsp, FixedArray **defaultsp) -{ - Local args(zone_, FixedArray::New(zone_, sig.parameters()->length(), Heap::Tenure_Old)); - if (!args) - return false; - - Local defaults(zone_); - - // Apply types to arguments. - for (size_t i = 0; i < sig.parameters()->length(); i++) { - Parameter *parameter = sig.parameters()->at(i); - -#if 0 - Local type(zone_, bindType(parameter->variable()->pos(), parameter->type())); - if (!type) - return false; - - if (parameter->reference()) { - if (parameter->dimensions()) { - cc_.reportError(parameter->variable()->pos(), Message_ArrayCantBeReference); - return false; - } - - if (type->isStruct()) { - cc_.reportError(parameter->variable()->pos(), Message_StructCantBeReference); - return false; - } - - type = zone_->types()->getReference(type, parameter->isConst()); - if (!type) - return false; - } else if (parameter->dimensions()) { - type = buildArrayType(type, parameter->dimensions(), parameter->isConst()); - if (!type) - return false; - - if (kind == TOK_NATIVE && type->levels() > 1) { - cc_.reportError(parameter->variable()->pos(), Message_MultiArrayInNative); - return false; - } - } - - if (IsScalarString(type)) { - cc_.reportError(node->pos(), Message_CannotDeclareStringCell); - return false; - } - - args->set(zone_, i, type); - parameter->variable()->setType(type); - - if (parameter->initializer()) { - if (!defaults) { - defaults = FixedArray::New(zone_, sig.parameters()->length(), Heap::Tenure_Old); - if (!defaults) - return false; - } - - Local box(zone_, evaluateDefaultParameter(type, parameter)); - if (!box) - return false; - - defaults->set(zone_, i, box); - } -#endif - } - - *argsp = args; - *defaultsp = defaults; - - return true; -} - -void -SemanticAnalysis::visit(FunctionTypeStatement *node) -{ - if (node->decl()->type()) - return; - - Local args(zone_); - Local defaults(zone_); - - const FunctionSignature &sig= node->signature(); - if (!buildFunctionArgs(node, - TOK_STOCK, - sig, - args.address(), - defaults.address())) - { - return; - } - -#if 0 - Local returnType(zone_); - if (sig.returnType()) - returnType = bindType(node->pos(), sig.returnType()); - else - returnType = zone_->types()->getPrimitive(PrimitiveType_Int32); - - if (!returnType) - return; - - Local funtype(zone_, zone_->types()->newFunction(returnType, args, defaults)); - if (!funtype) - return; - - node->decl()->setType(funtype); -#endif -} - -Type * -SemanticAnalysis::buildArrayType(Handle type, ExpressionList *dimensions, bool isConst) -{ - Local arrayType(zone_, type); - - unsigned dimcount = dimensions->length(); - if (type->isArray()) - dimcount += type->levels(); - - if (dimcount > MAX_ARRAY_DEPTH) { - cc_.reportError(dimensions->at(dimensions->length() - 1)->pos(), - Message_MaximumArrayDepth); - return false; - } - - // Note: constness is sort of bogus. Fix this sometime. - for (size_t i = dimensions->length() - 1; i < dimensions->length(); i--) { - Expression *expr = dimensions->at(i); - if (!expr) { - arrayType = zone_->types()->newArray(arrayType, Type::DYNAMIC_ARRAY_SIZE, isConst); - if (!arrayType) - return NULL; - - continue; - } - - BoxedPrimitive box; - if (!EvaluateForConstant(expr, &box) || box.type() != PrimitiveType_Int32) { - cc_.reportError(expr->pos(), Message_ArraySizeMustBeConstant); - return NULL; - } - - if (box.toInt() <= 0) { - cc_.reportError(expr->pos(), Message_BadArraySize); - return NULL; - } - - arrayType = zone_->types()->newArray(arrayType, box.toInt(), isConst); - if (!arrayType) - return NULL; - } - - return arrayType; -} - -bool -SemanticAnalysis::preanalyze(FunctionStatement *node) -{ - Local args(zone_); - Local defaults(zone_); - - const FunctionSignature &sig = node->signature(); - if (!buildFunctionArgs(node, - node->fun()->token(), - sig, - args.address(), - defaults.address())) - { - return false; - } - -#if 0 - Local returnType(zone_); - if (sig.returnType() || - node->hasExplicitReturn() || - node->fun()->isNative()) - { - returnType = bindType(node->pos(), sig.returnType()); - } else { - returnType = zone_->types()->getVoid(); - } - if (!returnType) - return false; - - if (sig.returnDimensions()) { - returnType = buildArrayType(returnType, sig.returnDimensions(), false); - if (!returnType) - return false; - } - - if (IsScalarString(returnType)) { - cc_.reportError(node->pos(), Message_CannotDeclareStringCell); - return false; - } - - Local funtype(zone_); - if (node->fun()->isNative()) - funtype = zone_->types()->newNative(returnType, args, defaults, node->fun()->isNativeVariadic()); - else - funtype = zone_->types()->newFunction(returnType, args, defaults); - if (!funtype) - return false; - - // If the function already has a type, it was forward declared before, and - // now this is our chance to type check the second declaration. - if (node->fun()->type()) { - Local forward(zone_, node->fun()->type()); - if (!Type::Compare(funtype, forward)) { - cc_.reportError(node->pos(), Message_ForwardDeclareMismatches); - return false; - } - node->fun()->setForwardType(forward); - } - - node->fun()->setType(funtype); -#endif - - return true; -} - -void -SemanticAnalysis::visit(FunctionStatement *node) -{ - function_ = node->fun(); - scope_ = node->scope(); - - ControlScope control(&control_); - if (node->body()) { - node->body()->accept(this); - - ControlStatus status = control.status(); - if (status.returns() == Returns_Sometimes) { - cc_.reportError(node->pos(), Message_NotAllPathsReturnValue); - } else if (status.returns() == Returns_Never) { - if (!function_->type()->returnType()->isVoid()) - cc_.reportError(node->pos(), Message_MissingReturnValue); - } - } - - scope_ = NULL; - function_ = NULL; -} - -void -SemanticAnalysis::typeMismatch(const SourcePosition &pos, Handle actual, Handle given) -{ - cc_.reportError(pos, Message_TypeMismatch, - GetTypeName(actual), - GetTypeName(given)); -} - -bool -SemanticAnalysis::typeCheckArrayAssignment(const SourcePosition &pos, Handle argGiven, - Handle argActual, bool forAssign) -{ - Local actual(zone_, argActual); - Local given(zone_, argGiven); - - if (!given->isArray()) { - cc_.reportError(pos, Message_ExpectedArray); - return false; - } - if (given->levels() != actual->levels()) { - cc_.reportError(pos, Message_ArrayDimensionsMismatch); - return false; - } - - unsigned level = 1; - while (given->isArray()) { - if (actual->isFixedLength()) { - if (forAssign) { - if (!given->isFixedLength()) { - cc_.reportError(pos, Message_ArrayDimensionSizeMismatch, level); - return false; - } - - if (actual->isCharArray()) { - // String assignment can have a truncated |given|. - if (given->fixedLength() > actual->fixedLength()) { - cc_.reportError(pos, Message_ArrayAssignmentTooBig); - return false; - } - } else { - if (given->fixedLength() != actual->fixedLength()) { - cc_.reportError(pos, Message_ArrayDimensionSizeMismatch, level); - return false; - } - } - } - } else if (given->isFixedLength()) { - // We do not allow assignments in the form: - // [][] = [][Y] - // - // Otherwise, the following code could break the size guarantees - // of the right-hand side: - // - // new x[][5]; - // new y[][]; - // new z[3]; - // - // y = x; - // y[0] = z; - // - // Now |x[0]| contains a 4-length array. - if (level > 1) { - cc_.reportError(pos, Message_ArrayDimensionSizeMismatch, level); - return false; - } - } - given = given->contained(); - actual = actual->contained(); - level++; - } - - return typeCheckAssignment(pos, given, actual, forAssign); -} - -bool -SemanticAnalysis::typeCheckAssignment(const SourcePosition &pos, Handle argGiven, - Handle argActual, bool forAssign) -{ - Local actual(zone_, argActual); - Local given(zone_, argGiven); - - if (actual->isArray()) - return typeCheckArrayAssignment(pos, given, actual, forAssign); - - if (given->isArray()) { - cc_.reportError(pos, Message_CannotAssignArrayToScalar); - return false; - } - - // The given type may not have been dereferenced. - if (given->isReference()) - given = given->contained(); - if (actual->isReference()) - actual = actual->contained(); - - // We coerce unchecked to a few things. - if (actual->isUnchecked() || given->isUnchecked()) { - Type *check = actual->isUnchecked() ? given : actual; - if (check->isUnchecked() || - check->isEnum() || - check->isInt32() || - check->isBool() || - check->isFloat()) - { - return true; - } - typeMismatch(pos, actual, given); - return false; - } - - if (given->isFunction() && actual->isFunction()) { - if (!Type::Compare(given, actual)) { - typeMismatch(pos, actual, given); - return false; - } - return true; - } - - if (actual->isEnum()) { - if (!given->isEnum() || actual != given) { - typeMismatch(pos, actual, given); - return false; - } - return true; - } - - if (actual->isStruct()) { - if (!given->isStruct() || actual != given) { - typeMismatch(pos, actual, given); - return false; - } - return true; - } - - if (given->isEnum() || given->isFunction() || given->isStruct()) { - typeMismatch(pos, actual, given); - return false; - } - - // This coercion is trivial. - if (given->isBool() && actual->isInt32()) - return true; - - if (given->primitive() != actual->primitive()) { - typeMismatch(pos, actual, given); - return false; - } - - return true; -} - -bool -SemanticAnalysis::typeCheckParameter(AstNode *node, Handle given, Handle actual) -{ - return typeCheckAssignment(node->pos(), given, actual, false); -} - -bool -SemanticAnalysis::checkArgumentCount(Handle type, unsigned actual) -{ - if (type->parameters()->length() == actual) - return true; - - if (actual > type->parameters()->length()) { - if (!type->isNative() || !type->isNativeVariadic()) - return false; - return true; - } - - if (!type->defaults()) - return false; - - // Missing arguments. See if there's a default argument for each missing - // actual. - for (unsigned i = actual; i < type->parameters()->length(); i++) { - if (!type->defaults()->at(i)) - return false; - } - - return true; -} - -void -SemanticAnalysis::visit(CallExpression *node) -{ - Local type(zone_, visitForLoad(node->callee())); - if (!type) - return; - - if (!type->isFunction()) { - cc_.reportError(node->pos(), Message_CalleeNotFunction); - return; - } - - if (!checkArgumentCount(type, node->arguments()->length())) { - cc_.reportError(node->pos(), Message_ArgumentCountMismatch); - return; - } - - // NamedFunction *fun = node->callee()->toNameProxy()->binding()->toNamedFunction(); - // if (fun->isForward()) { - // cc_.reportError(node->pos(), Message_ForwardNotImplemented, fun->name()->chars()); - // return; - // } - - for (unsigned i = 0; i < node->arguments()->length(); i++) { - // Note: we don't compute references unless the callee needs a - // reference type. - Expression *expression = node->arguments()->at(i); - Local given(zone_, visitAndApplyType(expression)); - if (!given) - return; - - // Since we skip a load, check for a call expression that returns void. - if (given->isVoid()) { - assert(expression->isCallExpression()); - CallExpression *call = expression->toCallExpression(); - cc_.reportError(call->callee()->pos(), Message_FunctionDoesNotReturnValue); - return; - } - - bool mustComputeReference; - if (i >= type->parameters()->length()) { - // Special handling for natives. Arguments past the formal must - // always have references computed. However, arrays are already - // references for natives. - assert(type->isNative() && type->isNativeVariadic()); - mustComputeReference = !given->isReference() && !given->isArray(); - } else { - Local actual(zone_, type->parameterAt(i)); - - if (expression->isIndexExpression()) { - // Disabled until we can make it work. -#if 0 - if (actual->isArray()) { - // We special case the ability to pass x[y] into x[], which - // creates an array slice. We only do this if we know the - // assignment would otherwise not type-check. - IndexExpression *index = expression->toIndexExpression(); - if (index->left()->type()->levels() == actual->levels()) - given = index->left()->type(); - } else -#endif - if (actual->isReference()) { - cc_.reportError(expression->pos(), Message_CannotComputeIndexRef); - return; - } - } - - if (!typeCheckParameter(expression, given, actual)) - return; - mustComputeReference = actual->isReference() && !given->isReference(); - } - - // If the actual is a reference, and the given is not, we'll need to - // compute a reference, which may be allocated on the stack. - if (mustComputeReference) - scope_->setUsesLifoHeap(); - } - - node->setType(type->returnType()); -} - -void -SemanticAnalysis::ControlScope::inherit(const ControlStatus &other) -{ - if (other.returns() < status().returns()) - return; - - if (other.terminates()) - terminate(other.returns()); - else - returns(other.returns()); -} - -void -SemanticAnalysis::ControlScope::merge(const ControlStatus &left, const ControlStatus &right) -{ - // If both sides terminate in a return, we consider this a return as well. - if (left.returns() == Returns_Always && right.returns() == Returns_Always) { - terminate(Returns_Always); - return; - } - - ReturnStatus status = (left.returns() == Returns_Never && right.returns() == Returns_Never) - ? Returns_Never - : Returns_Sometimes; - - if (left.terminates() && right.terminates()) - terminate(status); - else - returns(status); -} - -void -SemanticAnalysis::visit(IfStatement *node) -{ - Local type(zone_, visitForLoad(node->condition())); - if (!type) - return; - - if (!canTest(node, type)) - return; - - ControlStatus trueBranch = visitForControl(node->ifTrue()); - ControlStatus falseBranch = ControlStatus(false, Returns_Never); - - if (node->ifFalse()) - falseBranch = visitForControl(node->ifFalse()); - - control_->merge(trueBranch, falseBranch); -} - -void -SemanticAnalysis::visit(IndexExpression *node) -{ - Local left(zone_, visitForLoad(node->left())); - Local right(zone_, visitForLoad(node->right())); - - if (!left || !right) - return; - - if (!right->isInt32() && !right->isEnum()) { - cc_.reportError(node->right()->pos(), Message_IndexMustBeInteger); - return; - } - if (!left->isArray()) { - cc_.reportError(node->left()->pos(), Message_IndexBaseMustBeArray); - return; - } - - // Note: char arrays are automatically up-casted to return an int32. - if (left->isCharArray()) - node->setType(zone_->types()->getPrimitive(PrimitiveType_Int32)); - else - node->setType(left->contained()); -} - -void -SemanticAnalysis::visit(FieldExpression *node) -{ - Local left(zone_, visitForLoad(node->left())); - if (!left) - return; - - if (!left->isStruct()) { - cc_.reportError(node->pos(), Message_PropertyNotFound, node->field()->chars(), - GetTypeName(left)); - return; - } - - Local field(zone_, node->field()); - - unsigned offset; - Local desc(zone_, left->lookupField(field, &offset)); - if (!desc) { - cc_.reportError(node->pos(), Message_PropertyNotFound, node->field()->chars(), - GetTypeName(left)); - return; - } - - node->setFieldIndex(offset); - node->setType(desc->type()); -} - -void -SemanticAnalysis::visit(EnumStatement *node) -{ - Local name(zone_, node->name()); - Local type(zone_); - if (name) - type = zone_->types()->newEnum(name); - else - type = zone_->types()->getPrimitive(PrimitiveType_Int32); - - if (!type) - return; - - if (node->decl()) - node->decl()->setType(type); - - int value = 0; - for (size_t i = 0; i < node->entries()->length(); i++) { - EnumStatement::Entry &entry = node->entries()->at(i); - - BoxedPrimitive box = BoxedPrimitive::Int(value); - if (entry.expr) { - entry.expr->accept(this); - if (!EvaluateForConstant(entry.expr, &box) || box.type() != PrimitiveType_Int32) { - cc_.reportError(entry.expr->pos(), Message_EnumFieldNotConstant); - return; - } - - assert(box.type() == PrimitiveType_Int32); - value = box.toInt(); - } - -#if 0 - entry.name->set(type, box); -#endif - value++; - } -} - -static int -SortCaseIntValues(const void *p1, const void *p2) -{ - CaseValue *v1 = (CaseValue *)p1; - CaseValue *v2 = (CaseValue *)p2; - - return v1->box.toInt() - v2->box.toInt(); -} - -void -SemanticAnalysis::visit(SwitchStatement *node) -{ - Local caseType(zone_, visitForLoad(node->expression())); - if (!caseType) - return; - - if (!caseType->isEnum() && !caseType->isInt32()) { - cc_.reportError(node->pos(), Message_CaseMustBeInteger); - return; - } - - PoolList *values = new (zone_->pool()) PoolList(); - ControlStatus status = ControlStatus(false, Returns_Never); - - Local check(zone_); - for (size_t i = 0; i < node->cases()->length(); i++) { - Case *c = node->cases()->at(i); - - check = visitForLoad(c->expression()); - if (!check) - return; - - if (!typeCheckAssignment(c->expression()->pos(), check, caseType, false)) - return; - - CaseValue v(i); - if (!EvaluateForConstant(c->expression(), &v.box)) { - cc_.reportError(c->expression()->pos(), Message_CaseMustBeConstant); - return; - } - if (!values->append(v)) - return; - - if (c->others()) { - for (size_t j = 0; j < c->others()->length(); j++) { - Expression *expr = c->others()->at(j); - check = visitForLoad(expr); - if (!check) - return; - - if (!typeCheckAssignment(expr->pos(), check, caseType, false)) - return; - - CaseValue v(i); - if (!EvaluateForConstant(expr, &v.box)) { - cc_.reportError(expr->pos(), Message_CaseMustBeConstant); - return; - } - if (!values->append(v)) - return; - } - } - - ControlStatus latest = visitForControl(c->statement()); - - if (i != 0) - control_->merge(status, latest); - - status = latest; - } - - if (node->defaultCase()) { - ControlStatus latest = visitForControl(node->defaultCase()); - - if (node->cases()->length()) { - // If we had any other cases, we must merge. - control_->merge(status, latest); - } else { - // Otherwise, control-flow always flows through here. - control_->inherit(latest); - } - } else if (node->cases()->length()) { - control_->merge(status, ControlStatus(false, Returns_Never)); - } - - if (values->length() > 1) { - qsort(values->buffer(), values->length(), sizeof(CaseValue), SortCaseIntValues); - - for (size_t i = 1; i < values->length(); i++) { - CaseValue &prev = values->at(i - 1); - CaseValue &cur = values->at(i); - if (prev.box.toInt() == cur.box.toInt()) { - cc_.reportError(node->cases()->at(cur.statement)->expression()->pos(), Message_CaseHasSameValue); - return; - } - } - } - - node->setCaseValueList(values); -} - -void -SemanticAnalysis::visit(StructureStatement *node) -{ -#if 0 - // If this node already has a type, it was pre-analyzed, so skip it. - if (node->decl()->type()) - return; - - FieldList *fields = node->fields(); - Local desctable(zone_, FixedArray::New(zone_, fields->length(), Heap::Tenure_Old)); - if (!desctable) - return; - - Vector composedList; - StructureBufferBuilder builder; - - Local type(zone_); - Local name(zone_); - Local desc(zone_); - for (size_t i = 0; i < fields->length(); i++) { - StructureStatement::Field *field = fields->at(i); - - type = bindType(field->pos(), field->type()); - if (!type) - return; - - if (field->dimensions()) { - type = buildArrayType(type, field->dimensions(), false); - if (!type) - return; - - if (!ArrayMap::Attach(zone_, type)) - return; - } - - if (IsScalarString(type)) { - cc_.reportError(field->pos(), Message_CannotDeclareStringCell); - return; - } - - unsigned offset; - if (!builder.add(type, &offset)) - return; - - name = field->name(); - desc = Descriptor::New(zone_, name, type, offset); - if (!desc) - return; - - desctable->set(zone_, i, desc); - - if (type->isStruct() || type->isArray()) { - if (!composedList.append(i)) - return; - } - } - - Local composed(zone_); - if (composedList.length()) { - composed = FixedArray::New(zone_, composedList.length(), Heap::Tenure_Old); - if (!composed) - return; - - for (unsigned i = 0; i < composedList.length(); i++) - composed->set(zone_, i, desctable->at(i)); - } - - Local map(zone_, StructMap::New(zone_, builder, composed)); - if (!map) - return; - - name = node->decl()->name(); - type = zone_->types()->newStruct(name, desctable, map); - if (!type) - return; - - map->setType(type); - node->decl()->setType(type); -#endif -} - -void -SemanticAnalysis::visit(StringLiteral *node) -{ - Local type(zone_, zone_->types()->getPrimitive(PrimitiveType_Char)); - if (!type) - return; - - type = Type::NewArray(zone_, type, node->string()->length() + 1, true); - if (!type) - return; - - node->setType(type); -} - -void -SemanticAnalysis::visit(FloatLiteral *node) -{ - Local type(zone_, zone_->types()->getPrimitive(PrimitiveType_Float)); - if (!type) - return; - - node->setType(type); -} - -void -SemanticAnalysis::visit(BreakStatement *node) -{ - if (!loop_) { - cc_.reportError(node->pos(), Message_BreakOutsideLoop); - return; - } - - // Note: ignore breaks that occur in unreachable control-flow. - if (!control_->status().terminates()) - loop_->addBreak(); - - control_->terminate(Returns_Never); -} - -void -SemanticAnalysis::visit(ContinueStatement *node) -{ - if (!loop_) { - cc_.reportError(node->pos(), Message_ContinueOutsideLoop); - return; - } - - // Note: ignore continues that occur in unreachable control-flow. - if (!control_->status().terminates()) - loop_->addContinue(); - - control_->terminate(Returns_Never); -} - -void -SemanticAnalysis::visit(ArrayLiteral *node) -{ - // We should never visit this. - cc_.reportError(node->pos(), Message_UnexpectedArrayLiteral); -} - -void -SemanticAnalysis::visit(ImportStatement *node) -{ -} - +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include +#include "SemanticAnalysis.h" +#include "../Types.h" +#include "../Zone.h" +#include "../TypeManager.h" +#include "../Strings.h" +#include "../Box.h" +#include "../Array.h" +#include "../Heap-inl.h" +#include "Scopes.h" +#include "NameBinding.h" + +using namespace ke; + +SemanticAnalysis::SemanticAnalysis(Zone *zone, CompileContext &cc, ParseTree *tree) + : zone_(zone), + cc_(cc), + tree_(tree), + scope_(NULL), + loop_(NULL) +{ +} + +bool +SemanticAnalysis::analyze() +{ + assert(false); + return false; +} + + +#if 0 + +static inline bool +IsPrimitiveType(Type *type, PrimitiveType primitive) +{ + if (!type) + return false; + + if (!type->isPrimitive()) + return false; + + return primitive == type->primitive(); +} + +static inline bool +EvaluateBinaryOperation(TokenKind kind, const BoxedPrimitive &left, const BoxedPrimitive &right, + BoxedPrimitive *out) +{ + switch (kind) { + case TOK_PLUS: + *out = BoxedPrimitive::Int(left.toInt() + right.toInt()); + return true; + + case TOK_SHL: + *out = BoxedPrimitive::Int(left.toInt() << right.toInt()); + return true; + + default: + return false; + } +} + +static inline bool +EvaluateUnaryOperation(TokenKind kind, const BoxedPrimitive &inner, BoxedPrimitive *out) +{ + switch (kind) { + case TOK_MINUS: + *out = BoxedPrimitive::Int(-inner.toInt()); + return true; + + default: + return false; + } +} + +static bool +EvaluateForConstant(Expression *expression, BoxedPrimitive *out) +{ + switch (expression->kind()) { + case AstNode::kBinaryExpression: + { + BoxedPrimitive left; + BoxedPrimitive right; + BinaryExpression *b = expression->toBinaryExpression(); + if (!EvaluateForConstant(b->left(), &left) || + !EvaluateForConstant(b->right(), &right)) + { + return false; + } + return EvaluateBinaryOperation(b->token(), left, right, out); + } + + case AstNode::kUnaryExpression: + { + BoxedPrimitive inner; + UnaryExpression *u = expression->toUnaryExpression(); + if (!EvaluateForConstant(u->expression(), &inner)) + return false; + return EvaluateUnaryOperation(u->token(), inner, out); + } + + case AstNode::kNameProxy: + { + NameProxy *proxy = expression->toNameProxy(); + if (proxy->binding()->kind() != BoundName::CONSTANT) + return false; + + NamedConstant *c = proxy->binding()->toNamedConstant(); + if (!IsPrimitiveType(c->type(), PrimitiveType_Int32)) + return false; + + *out = c->value(); + return true; + } + + case AstNode::kBooleanLiteral: + *out = BoxedPrimitive::Int(expression->toBooleanLiteral()->token() == TOK_TRUE ? 1 : 0); + return true; + + case AstNode::kIntegerLiteral: + *out = BoxedPrimitive::Int(expression->toIntegerLiteral()->value()); + return true; + + default: + return false; + } +} + +SemanticAnalysis::SemanticAnalysis(Zone *zone, CompileContext &cc, ParseTree *tree) + : zone_(zone), + cc_(cc), + tree_(tree), + scope_(NULL), + loop_(NULL) +{ +} + +bool +SemanticAnalysis::analyze() +{ + if (cc_.failures()) + return false; + + scope_ = tree_->globalScope(); + assert(scope_->kind() == Scope::GLOBAL); + + for (size_t i = 0; i < tree_->statements()->length(); i++) { + Statement *statement = tree_->statements()->at(i); + if (statement->isFunctionStatement()) { + FunctionStatement *funstmt = statement->toFunctionStatement(); + if (!preanalyze(funstmt)) + return false; + } else if (statement->isEnumStatement()) { + EnumStatement *enumstmt = statement->toEnumStatement(); + + // We pre-fill global enum types, since they may occur in function + // signatures. This should probably be somewhere else... + enumstmt->accept(this); + } else if (statement->isStructureStatement()) { + StructureStatement *node = statement->toStructureStatement(); + node->accept(this); + if (!node->decl()->type()) + return false; + } else if (statement->isFunctionTypeStatement()) { + FunctionTypeStatement *node = statement->toFunctionTypeStatement(); + node->accept(this); + if (!node->decl()->type()) + return false; + } + } + + for (size_t i = 0; i < tree_->statements()->length(); i++) { + Statement *statement = tree_->statements()->at(i); + + // We can't double-process these. Maybe put this check somewhere else. + if (statement->isEnumStatement()) + continue; + + statement->accept(this); + } + + if (!determineLiveFunctions()) + return false; + + return !cc_.failures(); +} + +bool +SemanticAnalysis::determineLiveFunctions() +{ + Vector worklist; + + // Add the root set. + for (size_t i = 0; i < tree_->statements()->length(); i++) { + Statement *statement = tree_->statements()->at(i); + if (!statement->isFunctionStatement()) + continue; + + FunctionStatement *stmt = statement->toFunctionStatement(); + NamedFunction *fun = stmt->fun(); + if (!fun->used()) + continue; + + for (size_t i = 0; i < fun->usedGlobals()->length(); i++) { + NamedFunction *name = fun->usedGlobals()->at(i)->toNamedFunction(); + + // Names in these lists are always unused, however, we mark them + // as used while inserting into the work queue, to avoid double- + // insertion. + if (name->used()) + continue; + + if (!worklist.append(name)) + return false; + + name->setUsed(); + } + } + + while (!worklist.empty()) { + NamedFunction *fun = worklist.popCopy(); + + for (size_t i = 0; i < fun->usedGlobals()->length(); i++) { + NamedFunction *name = fun->usedGlobals()->at(i)->toNamedFunction(); + if (name->used()) + continue; + if (!worklist.append(name)) + return false; + name->setUsed(); + } + } + + return true; +} + +Type * +SemanticAnalysis::bindType(const SourcePosition &pos, BoundName *name) +{ + if (!name) + return zone_->types()->getPrimitive(PrimitiveType_Int32); + + if (name->kind() == BoundName::UNBOUND) { + cc_.reportError(pos, Message_TypeNotFound, name->name()->chars()); + return NULL; + } + + if (name->kind() != BoundName::TYPE) { + cc_.reportError(pos, Message_IdentifierIsNotAType, name->name()->chars()); + return NULL; + } + + if (!name->type()) + return NULL; + + return name->type(); +} + +Type * +SemanticAnalysis::visitAndApplyType(Expression *expression) +{ + expression->accept(this); + return expression->type(); +} + +Type * +SemanticAnalysis::visitForLoad(Expression *expression) +{ + Local type(zone_, visitAndApplyType(expression)); + if (!type) + return NULL; + + if (type->isReference()) { + expression->setType(type->contained()); + } else if (expression->isCallExpression()) { + if (type->isVoid()) { + CallExpression *call = expression->toCallExpression(); + cc_.reportError(call->callee()->pos(), Message_FunctionDoesNotReturnValue); + return NULL; + } + } + + return expression->type(); +} + +void +SemanticAnalysis::unaryTypeError(AstNode *node, TokenKind token, Handle type) +{ + const char *op = TokenNames[token]; + const char *name = GetTypeName(type); + + cc_.reportError(node->pos(), Message_UnaryTypeError, op, name); +} + +void +SemanticAnalysis::binaryTypeError(AstNode *node, TokenKind token, Handle left, Handle right) +{ + const char *op = TokenNames[token]; + const char *leftName = GetTypeName(left); + const char *rightName = GetTypeName(right); + + cc_.reportError(node->pos(), Message_BinaryTypeError, op, leftName, rightName); +} + +Type * +SemanticAnalysis::typeCheckBinary(AstNode *node, TokenKind token, Handle left, Handle right) +{ + if ((token >= TOK_PLUS && token <= TOK_SLASH) || + (token >= TOK_ASSIGN_ADD && token <= TOK_ASSIGN_DIV)) + { + // If either side is a float, the other side must be a float or int. + if (left->isFloat() || right->isFloat()) { + Type *floatType = left->isFloat() ? left : right; + Type *other = left->isFloat() ? right : left; + if (other->isFloat() || other->isInt32()) + return floatType; + } + } + + if (token == TOK_OR || token == TOK_AND) { + if (!canTest(node, left) || !canTest(node, right)) + return NULL; + return zone_->types()->getPrimitive(PrimitiveType_Bool); + } + + if (token >= TOK_EQUALS && token <= TOK_GE) { + if ((left->isFloat() || left->isInt32()) && + (right->isFloat() || right->isInt32())) + { + return zone_->types()->getPrimitive(PrimitiveType_Bool); + } + binaryTypeError(node, token, left, right); + return NULL; + } + + if (!left->isInt32() || !right->isInt32()) { + binaryTypeError(node, token, left, right); + return NULL; + } + + return left; +} + +bool +SemanticAnalysis::canTest(AstNode *node, Handle type) +{ + if (!type->isPrimitive() && !type->isEnum()) { + cc_.reportError(node->pos(), Message_ArrayIsNotBoolean); + return false; + } + return true; +} + +Type * +SemanticAnalysis::visitForLValue(Expression *expression) +{ + if (expression->isIndexExpression()) { + Type *type = visitAndApplyType(expression); + if (!type) + return NULL; + + // if (type->isConst()) { + // cc_.reportError(expression->pos(), Message_ReferenceIsConstant); + // return NULL; + // } + + return type; + } + + if (expression->isFieldExpression()) { + Type *type = visitAndApplyType(expression); + if (!type) + return NULL; + + // if (expression->toFieldExpression()->left()->type()->isConst()) { + // cc_.reportError(expression->pos(), Message_ReferenceIsConstant); + // return NULL; + // } + + return type; + } + + if (!expression->isNameProxy()) { + cc_.reportError(expression->pos(), Message_ExpectedLValue); + return NULL; + } + + NameProxy *proxy = expression->toNameProxy(); + if (!proxy->binding()->kind() == BoundName::VARIABLE) { + cc_.reportError(expression->pos(), Message_ExpectedLValue); + return NULL; + } + + Variable *var = proxy->binding()->toVariable(); + if (!var->type()) + return NULL; + + if (var->type()->isReference()) { + if (var->type()->isConst()) { + cc_.reportError(expression->pos(), Message_ReferenceIsConstant); + return NULL; + } + return var->type()->contained(); + } + return var->type(); +} + +SemanticAnalysis::ControlStatus +SemanticAnalysis::visitForControl(Statement *statement) +{ + ControlScope scope(&control_); + statement->accept(this); + return scope.status(); +} + +void +SemanticAnalysis::visit(NameProxy *name) +{ + BoundName *binding = name->binding(); + + if (binding->scope()->kind() == Scope::GLOBAL) { + if (!function_ || function_->isPublic()) { + // Whatever we're referencing must be kept alive no matter what. + // As such, we simply mark it used, and don't do anything special. + binding->setUsed(); + } else if (!binding->used() && binding->kind() == BoundName::FUNCTION) { + // Otherwise, we're inside a normal stock or function. Since we + // don't really know if we've been used yet, we shouldn't + // prematurely references as alive. Instead, we keep a list of + // which names have been used, so we can mark them later if needed. + // + // Note that we skip this step if the binding is already in the + // root set. + if (!function_->addUsedGlobal(binding)) + return; + } + } + + name->setType(binding->type()); +} + +void +SemanticAnalysis::visit(Assignment *node) +{ + Local exprType(zone_, visitForLoad(node->expression())); + if (!exprType) + return ; + + Local type(zone_, visitForLValue(node->lvalue())); + if (!type) + return; + + if (node->token() != TOK_ASSIGN) { + if (!typeCheckBinary(node, node->token(), type, exprType)) + return; + } + + if (!typeCheckAssignment(node->pos(), exprType, type, true)) + return; + + node->setType(type); +} + +void +SemanticAnalysis::visit(IncDecExpression *node) +{ + Local type(zone_, visitForLValue(node->expression())); + if (!type) + return; + + assert(!type->isReference()); + + if (!type->isInt32() && !type->isFloat()) { + unaryTypeError(node, node->token(), type); + return; + } + + node->setType(type); +} + +void +SemanticAnalysis::visit(UnaryExpression *node) +{ + Local type(zone_, visitForLoad(node->expression())); + if (!type) + return; + + switch (node->token()) + { + case TOK_NOT: + if (!canTest(node->expression(), type)) + return; + type = zone_->types()->getPrimitive(PrimitiveType_Int32); + if (!type) + return; + node->setType(type); + break; + + case TOK_MINUS: + if (!type->isFloat() && !type->isInt32()) { + unaryTypeError(node, node->token(), type); + return; + } + node->setType(type); + break; + + case TOK_LABEL: + { + Local to(zone_, bindType(node->pos(), node->type())); + if (!to) + return; + + if ((!type->isFloat() && !type->isInt32OrEnum()) || + (!to->isFloat() && !to->isInt32OrEnum())) + { + unaryTypeError(node, node->token(), type); + return; + } + + node->setType(to); + break; + } + + case TOK_SIZEOF: + { + if (!type->isArray()) { + cc_.reportError(node->pos(), Message_SizeofNeedsArray); + return; + } + node->setType(zone_->types()->getPrimitive(PrimitiveType_Int32)); + break; + } + + default: + assert(node->token() == TOK_TILDE); + if (!type->isInt32()) { + unaryTypeError(node, node->token(), type); + return; + } + node->setType(type); + break; + } +} + +void +SemanticAnalysis::visit(TernaryExpression *node) +{ + Local type(zone_, visitForLoad(node->condition())); + if (!type) + return; + + if (!canTest(node->condition(), type)) + return; + + Local left(zone_, visitForLoad(node->left())); + Local right(zone_, visitForLoad(node->right())); + if (!left || !right) + return; + + if (left != right) { + binaryTypeError(node, TOK_QMARK, left, right); + return; + } +} + +void +SemanticAnalysis::visit(BinaryExpression *node) +{ + Local left(zone_, visitForLoad(node->left())); + Local right(zone_, visitForLoad(node->right())); + if (!left || !right) + return; + + Local result(zone_, typeCheckBinary(node, node->token(), left, right)); + if (!result) + return; + + node->setType(result); +} + +void +SemanticAnalysis::visit(ReturnStatement *node) +{ + // Set this early, so if we error we don't get more errors. + control_->returns(Returns_Always); + + if (!node->expression()) { + if (!function_->type()->returnType()->isVoid()) + cc_.reportError(node->pos(), Message_NotAllPathsReturnValue); + return; + } + + Local type(zone_, visitForLoad(node->expression())); + if (!type) + return; + + Local returnType(zone_, function_->type()->returnType()); + if (!typeCheckAssignment(node->pos(), type, returnType, true)) + return; +} + +void +SemanticAnalysis::visit(ForStatement *node) +{ + LoopScope loop(&loop_); + + if (node->scope()) + scope_ = node->scope(); + + if (node->initialization()) + node->initialization()->accept(this); + + if (node->condition()) { + Local type(zone_, visitForLoad(node->condition())); + if (!type || !canTest(node->condition(), type)) { + return; + } + } + + if (node->update()) + node->update()->accept(this); + + ControlStatus status = visitForControl(node->body()); + + // If there is a condition, we treat this like a while loop. Otherwise, + // join point is only reachable if the loop contains a break. The + // termination status doesn't tell us anything yet. + if (node->condition() || loop.breaks()) + control_->merge(status, ControlStatus(false, Returns_Never)); + else + control_->terminate(status.returns()); + + if (node->scope()) + scope_ = scope_->enclosing(); +} + +void +SemanticAnalysis::visit(WhileStatement *node) +{ + LoopScope loop(&loop_); + + Local type(zone_, visitForLoad(node->condition())); + if (!type || !canTest(node->condition(), type)) { + return; + } + + ControlStatus status = visitForControl(node->body()); + + // Detect loops that will always have a fork in the control-flow graph. + // This will be a |while| loop, or a |do-while| loop whose body does + // not terminate. + // + // For now, we treat these as an |if| regardless of the direction the + // branch can take. That means the control-flow is always conditional. + if (node->token() == TOK_WHILE || + !status.terminates() || + loop.breaks()) + { + control_->merge(status, ControlStatus(false, Returns_Never)); + } else { + // At this point, we've got a do-while loop with a |return| or + // |continue|, meaning that the rest of the block is unreachable. + control_->terminate(status.returns()); + } +} + +void +SemanticAnalysis::visit(BlockStatement *node) +{ + if (node->scope()) + scope_ = node->scope(); + for (size_t i = 0; i < node->statements()->length(); i++) { + Statement *statement = node->statements()->at(i); + statement->accept(this); + } + if (node->scope()) + scope_ = scope_->enclosing(); +} + +static inline bool +IsScalarString(Type *type) +{ + if (type->isPrimitive() && type->primitive() == PrimitiveType_Char) + return true; + if (type->isReference()) + return IsScalarString(type->contained()); + return false; +} + +static const int INFER_ARRAY_SIZE = 0; +static const int EVAL_ARRAY_SIZE = -1; +static const int DYNAMIC_ARRAY_SIZE = -2; + +bool +SemanticAnalysis::evaluateDimensions(ExpressionList *exprs, int dims[MAX_ARRAY_DEPTH]) +{ + bool hadEmptySize = false; + + for (size_t i = 0; i < exprs->length(); i++) { + Expression *expr = exprs->at(i); + if (!expr) { + dims[i] = INFER_ARRAY_SIZE; + hadEmptySize = true; + continue; + } + + BoxedPrimitive box; + if (EvaluateForConstant(expr, &box) && box.type() == PrimitiveType_Int32) { + int size = box.toInt(); + if (size <= 0) { + cc_.reportError(expr->pos(), Message_BadArraySize); + return false; + } + + dims[i] = size; + continue; + } + + Local exprType(zone_, visitForLoad(expr)); + if (!exprType) + return false; + + if (!IsPrimitiveType(exprType, PrimitiveType_Int32)) { + cc_.reportError(expr->pos(), Message_ArrayDimensionNotInteger); + return false; + } + + if (hadEmptySize) { + cc_.reportError(expr->pos(), Message_BadDynamicInitializer); + return false; + } + + dims[i] = EVAL_ARRAY_SIZE; + } + + return true; +} + +bool +SemanticAnalysis::inferFixedArrayLiteral(ArrayLiteral *literal, Handle contained, + int dims[MAX_ARRAY_DEPTH], + unsigned dimcount, unsigned level) +{ + if (dims[level] == EVAL_ARRAY_SIZE) { + cc_.reportError(literal->pos(), Message_CannotInitializeEvalArray); + return false; + } + + if (dims[level] == INFER_ARRAY_SIZE) { + // Strings always become dynamic arrays. + if (level != dimcount - 1 || !IsPrimitiveType(contained, PrimitiveType_Char)) + dims[level] = literal->expressions()->length(); + } else if (dims[level] == DYNAMIC_ARRAY_SIZE) { + cc_.reportError(literal->pos(), Message_CannotInitializeEvalArray); + return false; + } else { + if (literal->expressions()->length() != unsigned(dims[level])) { + cc_.reportError(literal->pos(), Message_ArrayDimensionSizeMismatch, level + 1); + return false; + } + } + + if (level == dimcount - 2 && IsPrimitiveType(contained, PrimitiveType_Char)) { + for (size_t i = 0; i < literal->expressions()->length(); i++) { + Expression *expr = literal->expressions()->at(i); + if (!expr->isStringLiteral()) { + cc_.reportError(expr->pos(), Message_ExpectedStringLiteral); + return false; + } + + StringLiteral *lit = expr->toStringLiteral(); + if (dims[level + 1] > 0 && + unsigned(dims[level]) < lit->string()->length()) + { + cc_.reportError(lit->pos(), Message_ArrayDimensionSizeMismatch, level + 2); + return false; + } + if (!visitAndApplyType(lit)) + return false; + } + } else if (level == dimcount - 1) { + Local type(zone_); + for (size_t i = 0; i < literal->expressions()->length(); i++) { + Expression *expr = literal->expressions()->at(i); + if (expr->isArrayLiteral()) { + cc_.reportError(expr->pos(), Message_ArrayDimensionsMismatch); + return false; + } + + if ((type = visitAndApplyType(expr)) == NULL) + return false; + + if (!typeCheckAssignment(expr->pos(), type, contained, true)) + return false; + } + } else { + for (size_t i = 0; i < literal->expressions()->length(); i++) { + Expression *expr = literal->expressions()->at(i); + if (!expr->isArrayLiteral()) { + cc_.reportError(expr->pos(), Message_ArrayDimensionsMismatch); + return false; + } + + assert(expr->toArrayLiteral()->isFixed()); + + if (!inferFixedArrayLiteral(expr->toArrayLiteral(), + contained, + dims, + dimcount, + level + 1)) + { + return false; + } + } + } + + return true; +} + +void +SemanticAnalysis::visit(VariableDeclaration *node) +{ + Local type(zone_, bindType(node->pos(), node->type())); + if (!type) + return; + +#if 0 + if (node->postDimensions() || type->isArray()) { + // The type could be an array type, so sum the type and postDimensions + // together. + unsigned dimcount = node->postDimensions()->length(); + if (type->isArray()) + dimcount += type->levels(); + if (dimcount > MAX_ARRAY_DEPTH) { + cc_.reportError(node->pos(), Message_MaximumArrayDepth); + return; + } + + // Take an initial guess at the array dimension sizes. + int dims[MAX_ARRAY_DEPTH]; + if (!evaluateDimensions(node->postDimensions(), dims)) + return; + + // Now add in the type's array dimensions. + Local contained(zone_, type); + for (size_t i = node->postDimensions()->length(); i < dimcount; i++) { + if (type->isFixedLength()) + dims[i] = type->fixedLength(); + else + dims[i] = DYNAMIC_ARRAY_SIZE; + contained = contained->contained(); + } + + Expression *initializer = node->initialization(); + if (initializer && + initializer->isArrayLiteral() && + initializer->toArrayLiteral()->isFixed()) + { + // Fixed array initializers are special; we use them to infer any + // missing information in the original array. We do not evaluate + // the array initializer as an expression. + if (!inferFixedArrayLiteral(initializer->toArrayLiteral(), + contained, + dims, + dimcount, + 0)) + { + return; + } + initializer = NULL; + } + + // We now have enough information to build the actual array type. + // Start from the original dimcount, not the one computed for + // initializers. + for (size_t i = node->postDimensions()->length() - 1; + i < node->postDimensions()->length(); + i--) + { + int size = dims[i] > 0 + ? dims[i] + : Type::DYNAMIC_ARRAY_SIZE; + + type = zone_->types()->newArray(type, size, false); + if (!type) + return; + } + + // Finally, if we had a non-fixed initializer, make sure it type + // checks. + if (initializer) { + Local right(zone_, visitForLoad(initializer)); + if (!right) + return; + + if (!typeCheckAssignment(node->pos(), right, type, true)) + return; + } + } else { + if (node->initialization()) { + Local right(zone_, visitForLoad(node->initialization())); + if (!right) + return; + if (!typeCheckAssignment(node->pos(), right, type, true)) + return; + } + + if (type->isPrimitive() && type->primitive() == PrimitiveType_Char) { + cc_.reportError(node->pos(), Message_CannotDeclareStringCell); + return; + } + } +#endif + +#if 0 + // If this variable is stack allocated, we could use the LIFO heap + // to allocate it. If that's the case, make sure to reserve a slot + // so we can unwind the LIFO heap at the end of this scope. + if (!node->variable()->needsHeapAllocation()) { + if (type->isArray() || type->isStruct()) + scope_->setUsesLifoHeap(); + } + + node->variable()->setType(type); +#endif +} + +void +SemanticAnalysis::visit(IntegerLiteral *node) +{ + node->setType(zone_->types()->getPrimitive(PrimitiveType_Int32)); +} + +void +SemanticAnalysis::visit(BooleanLiteral *node) +{ + node->setType(zone_->types()->getPrimitive(PrimitiveType_Bool)); +} + +void +SemanticAnalysis::visit(ExpressionStatement *node) +{ + visitAndApplyType(node->expression()); +} + +Box * +SemanticAnalysis::evaluateDefaultParameter(Handle type, Parameter *parameter) +{ + Local given(zone_, visitForLoad(parameter->initializer())); + + BoxedPrimitive prim; + if (EvaluateForConstant(parameter->initializer(), &prim)) { + if (!typeCheckAssignment(parameter->initializer()->pos(), given, type, false)) + return NULL; + + if (prim.type() == PrimitiveType_Int32) + return Box::New(zone_, given, prim.toInt()); + return Box::New(zone_, given, prim.toFloat()); + } else if (parameter->initializer()->isStringLiteral()) { + StringLiteral *literal = parameter->initializer()->toStringLiteral(); + + if (!typeCheckAssignment(parameter->initializer()->pos(), given, type, false)) + return NULL; + + Local map(zone_, ArrayMap::Attach(zone_, given)); + if (!map) + return NULL; + + Local array(zone_, + Array::NewString(zone_, + map, + literal->string()->raw(), + literal->string()->length(), + Heap::Tenure_Old)); + if (!array) + return NULL; + + return Box::NewObj(zone_, given, array, Heap::Tenure_Old); + } + + cc_.reportError(parameter->initializer()->pos(), Message_MustBeConstant); + return NULL; +} + +bool +SemanticAnalysis::buildFunctionArgs(AstNode *node, TokenKind kind, const FunctionSignature &sig, + FixedArray **argsp, FixedArray **defaultsp) +{ + Local args(zone_, FixedArray::New(zone_, sig.parameters()->length(), Heap::Tenure_Old)); + if (!args) + return false; + + Local defaults(zone_); + + // Apply types to arguments. + for (size_t i = 0; i < sig.parameters()->length(); i++) { + Parameter *parameter = sig.parameters()->at(i); + +#if 0 + Local type(zone_, bindType(parameter->variable()->pos(), parameter->type())); + if (!type) + return false; + + if (parameter->reference()) { + if (parameter->dimensions()) { + cc_.reportError(parameter->variable()->pos(), Message_ArrayCantBeReference); + return false; + } + + if (type->isStruct()) { + cc_.reportError(parameter->variable()->pos(), Message_StructCantBeReference); + return false; + } + + type = zone_->types()->getReference(type, parameter->isConst()); + if (!type) + return false; + } else if (parameter->dimensions()) { + type = buildArrayType(type, parameter->dimensions(), parameter->isConst()); + if (!type) + return false; + + if (kind == TOK_NATIVE && type->levels() > 1) { + cc_.reportError(parameter->variable()->pos(), Message_MultiArrayInNative); + return false; + } + } + + if (IsScalarString(type)) { + cc_.reportError(node->pos(), Message_CannotDeclareStringCell); + return false; + } + + args->set(zone_, i, type); + parameter->variable()->setType(type); + + if (parameter->initializer()) { + if (!defaults) { + defaults = FixedArray::New(zone_, sig.parameters()->length(), Heap::Tenure_Old); + if (!defaults) + return false; + } + + Local box(zone_, evaluateDefaultParameter(type, parameter)); + if (!box) + return false; + + defaults->set(zone_, i, box); + } +#endif + } + + *argsp = args; + *defaultsp = defaults; + + return true; +} + +void +SemanticAnalysis::visit(FunctionTypeStatement *node) +{ + if (node->decl()->type()) + return; + + Local args(zone_); + Local defaults(zone_); + + const FunctionSignature &sig= node->signature(); + if (!buildFunctionArgs(node, + TOK_STOCK, + sig, + args.address(), + defaults.address())) + { + return; + } + +#if 0 + Local returnType(zone_); + if (sig.returnType()) + returnType = bindType(node->pos(), sig.returnType()); + else + returnType = zone_->types()->getPrimitive(PrimitiveType_Int32); + + if (!returnType) + return; + + Local funtype(zone_, zone_->types()->newFunction(returnType, args, defaults)); + if (!funtype) + return; + + node->decl()->setType(funtype); +#endif +} + +Type * +SemanticAnalysis::buildArrayType(Handle type, ExpressionList *dimensions, bool isConst) +{ + Local arrayType(zone_, type); + + unsigned dimcount = dimensions->length(); + if (type->isArray()) + dimcount += type->levels(); + + if (dimcount > MAX_ARRAY_DEPTH) { + cc_.reportError(dimensions->at(dimensions->length() - 1)->pos(), + Message_MaximumArrayDepth); + return false; + } + + // Note: constness is sort of bogus. Fix this sometime. + for (size_t i = dimensions->length() - 1; i < dimensions->length(); i--) { + Expression *expr = dimensions->at(i); + if (!expr) { + arrayType = zone_->types()->newArray(arrayType, Type::DYNAMIC_ARRAY_SIZE, isConst); + if (!arrayType) + return NULL; + + continue; + } + + BoxedPrimitive box; + if (!EvaluateForConstant(expr, &box) || box.type() != PrimitiveType_Int32) { + cc_.reportError(expr->pos(), Message_ArraySizeMustBeConstant); + return NULL; + } + + if (box.toInt() <= 0) { + cc_.reportError(expr->pos(), Message_BadArraySize); + return NULL; + } + + arrayType = zone_->types()->newArray(arrayType, box.toInt(), isConst); + if (!arrayType) + return NULL; + } + + return arrayType; +} + +bool +SemanticAnalysis::preanalyze(FunctionStatement *node) +{ + Local args(zone_); + Local defaults(zone_); + + const FunctionSignature &sig = node->signature(); + if (!buildFunctionArgs(node, + node->fun()->token(), + sig, + args.address(), + defaults.address())) + { + return false; + } + +#if 0 + Local returnType(zone_); + if (sig.returnType() || + node->hasExplicitReturn() || + node->fun()->isNative()) + { + returnType = bindType(node->pos(), sig.returnType()); + } else { + returnType = zone_->types()->getVoid(); + } + if (!returnType) + return false; + + if (sig.returnDimensions()) { + returnType = buildArrayType(returnType, sig.returnDimensions(), false); + if (!returnType) + return false; + } + + if (IsScalarString(returnType)) { + cc_.reportError(node->pos(), Message_CannotDeclareStringCell); + return false; + } + + Local funtype(zone_); + if (node->fun()->isNative()) + funtype = zone_->types()->newNative(returnType, args, defaults, node->fun()->isNativeVariadic()); + else + funtype = zone_->types()->newFunction(returnType, args, defaults); + if (!funtype) + return false; + + // If the function already has a type, it was forward declared before, and + // now this is our chance to type check the second declaration. + if (node->fun()->type()) { + Local forward(zone_, node->fun()->type()); + if (!Type::Compare(funtype, forward)) { + cc_.reportError(node->pos(), Message_ForwardDeclareMismatches); + return false; + } + node->fun()->setForwardType(forward); + } + + node->fun()->setType(funtype); +#endif + + return true; +} + +void +SemanticAnalysis::visit(FunctionStatement *node) +{ + function_ = node->fun(); + scope_ = node->scope(); + + ControlScope control(&control_); + if (node->body()) { + node->body()->accept(this); + + ControlStatus status = control.status(); + if (status.returns() == Returns_Sometimes) { + cc_.reportError(node->pos(), Message_NotAllPathsReturnValue); + } else if (status.returns() == Returns_Never) { + if (!function_->type()->returnType()->isVoid()) + cc_.reportError(node->pos(), Message_MissingReturnValue); + } + } + + scope_ = NULL; + function_ = NULL; +} + +void +SemanticAnalysis::typeMismatch(const SourcePosition &pos, Handle actual, Handle given) +{ + cc_.reportError(pos, Message_TypeMismatch, + GetTypeName(actual), + GetTypeName(given)); +} + +bool +SemanticAnalysis::typeCheckArrayAssignment(const SourcePosition &pos, Handle argGiven, + Handle argActual, bool forAssign) +{ + Local actual(zone_, argActual); + Local given(zone_, argGiven); + + if (!given->isArray()) { + cc_.reportError(pos, Message_ExpectedArray); + return false; + } + if (given->levels() != actual->levels()) { + cc_.reportError(pos, Message_ArrayDimensionsMismatch); + return false; + } + + unsigned level = 1; + while (given->isArray()) { + if (actual->isFixedLength()) { + if (forAssign) { + if (!given->isFixedLength()) { + cc_.reportError(pos, Message_ArrayDimensionSizeMismatch, level); + return false; + } + + if (actual->isCharArray()) { + // String assignment can have a truncated |given|. + if (given->fixedLength() > actual->fixedLength()) { + cc_.reportError(pos, Message_ArrayAssignmentTooBig); + return false; + } + } else { + if (given->fixedLength() != actual->fixedLength()) { + cc_.reportError(pos, Message_ArrayDimensionSizeMismatch, level); + return false; + } + } + } + } else if (given->isFixedLength()) { + // We do not allow assignments in the form: + // [][] = [][Y] + // + // Otherwise, the following code could break the size guarantees + // of the right-hand side: + // + // new x[][5]; + // new y[][]; + // new z[3]; + // + // y = x; + // y[0] = z; + // + // Now |x[0]| contains a 4-length array. + if (level > 1) { + cc_.reportError(pos, Message_ArrayDimensionSizeMismatch, level); + return false; + } + } + given = given->contained(); + actual = actual->contained(); + level++; + } + + return typeCheckAssignment(pos, given, actual, forAssign); +} + +bool +SemanticAnalysis::typeCheckAssignment(const SourcePosition &pos, Handle argGiven, + Handle argActual, bool forAssign) +{ + Local actual(zone_, argActual); + Local given(zone_, argGiven); + + if (actual->isArray()) + return typeCheckArrayAssignment(pos, given, actual, forAssign); + + if (given->isArray()) { + cc_.reportError(pos, Message_CannotAssignArrayToScalar); + return false; + } + + // The given type may not have been dereferenced. + if (given->isReference()) + given = given->contained(); + if (actual->isReference()) + actual = actual->contained(); + + // We coerce unchecked to a few things. + if (actual->isUnchecked() || given->isUnchecked()) { + Type *check = actual->isUnchecked() ? given : actual; + if (check->isUnchecked() || + check->isEnum() || + check->isInt32() || + check->isBool() || + check->isFloat()) + { + return true; + } + typeMismatch(pos, actual, given); + return false; + } + + if (given->isFunction() && actual->isFunction()) { + if (!Type::Compare(given, actual)) { + typeMismatch(pos, actual, given); + return false; + } + return true; + } + + if (actual->isEnum()) { + if (!given->isEnum() || actual != given) { + typeMismatch(pos, actual, given); + return false; + } + return true; + } + + if (actual->isStruct()) { + if (!given->isStruct() || actual != given) { + typeMismatch(pos, actual, given); + return false; + } + return true; + } + + if (given->isEnum() || given->isFunction() || given->isStruct()) { + typeMismatch(pos, actual, given); + return false; + } + + // This coercion is trivial. + if (given->isBool() && actual->isInt32()) + return true; + + if (given->primitive() != actual->primitive()) { + typeMismatch(pos, actual, given); + return false; + } + + return true; +} + +bool +SemanticAnalysis::typeCheckParameter(AstNode *node, Handle given, Handle actual) +{ + return typeCheckAssignment(node->pos(), given, actual, false); +} + +bool +SemanticAnalysis::checkArgumentCount(Handle type, unsigned actual) +{ + if (type->parameters()->length() == actual) + return true; + + if (actual > type->parameters()->length()) { + if (!type->isNative() || !type->isNativeVariadic()) + return false; + return true; + } + + if (!type->defaults()) + return false; + + // Missing arguments. See if there's a default argument for each missing + // actual. + for (unsigned i = actual; i < type->parameters()->length(); i++) { + if (!type->defaults()->at(i)) + return false; + } + + return true; +} + +void +SemanticAnalysis::visit(CallExpression *node) +{ + Local type(zone_, visitForLoad(node->callee())); + if (!type) + return; + + if (!type->isFunction()) { + cc_.reportError(node->pos(), Message_CalleeNotFunction); + return; + } + + if (!checkArgumentCount(type, node->arguments()->length())) { + cc_.reportError(node->pos(), Message_ArgumentCountMismatch); + return; + } + + // NamedFunction *fun = node->callee()->toNameProxy()->binding()->toNamedFunction(); + // if (fun->isForward()) { + // cc_.reportError(node->pos(), Message_ForwardNotImplemented, fun->name()->chars()); + // return; + // } + + for (unsigned i = 0; i < node->arguments()->length(); i++) { + // Note: we don't compute references unless the callee needs a + // reference type. + Expression *expression = node->arguments()->at(i); + Local given(zone_, visitAndApplyType(expression)); + if (!given) + return; + + // Since we skip a load, check for a call expression that returns void. + if (given->isVoid()) { + assert(expression->isCallExpression()); + CallExpression *call = expression->toCallExpression(); + cc_.reportError(call->callee()->pos(), Message_FunctionDoesNotReturnValue); + return; + } + + bool mustComputeReference; + if (i >= type->parameters()->length()) { + // Special handling for natives. Arguments past the formal must + // always have references computed. However, arrays are already + // references for natives. + assert(type->isNative() && type->isNativeVariadic()); + mustComputeReference = !given->isReference() && !given->isArray(); + } else { + Local actual(zone_, type->parameterAt(i)); + + if (expression->isIndexExpression()) { + // Disabled until we can make it work. +#if 0 + if (actual->isArray()) { + // We special case the ability to pass x[y] into x[], which + // creates an array slice. We only do this if we know the + // assignment would otherwise not type-check. + IndexExpression *index = expression->toIndexExpression(); + if (index->left()->type()->levels() == actual->levels()) + given = index->left()->type(); + } else +#endif + if (actual->isReference()) { + cc_.reportError(expression->pos(), Message_CannotComputeIndexRef); + return; + } + } + + if (!typeCheckParameter(expression, given, actual)) + return; + mustComputeReference = actual->isReference() && !given->isReference(); + } + + // If the actual is a reference, and the given is not, we'll need to + // compute a reference, which may be allocated on the stack. + if (mustComputeReference) + scope_->setUsesLifoHeap(); + } + + node->setType(type->returnType()); +} + +void +SemanticAnalysis::ControlScope::inherit(const ControlStatus &other) +{ + if (other.returns() < status().returns()) + return; + + if (other.terminates()) + terminate(other.returns()); + else + returns(other.returns()); +} + +void +SemanticAnalysis::ControlScope::merge(const ControlStatus &left, const ControlStatus &right) +{ + // If both sides terminate in a return, we consider this a return as well. + if (left.returns() == Returns_Always && right.returns() == Returns_Always) { + terminate(Returns_Always); + return; + } + + ReturnStatus status = (left.returns() == Returns_Never && right.returns() == Returns_Never) + ? Returns_Never + : Returns_Sometimes; + + if (left.terminates() && right.terminates()) + terminate(status); + else + returns(status); +} + +void +SemanticAnalysis::visit(IfStatement *node) +{ + Local type(zone_, visitForLoad(node->condition())); + if (!type) + return; + + if (!canTest(node, type)) + return; + + ControlStatus trueBranch = visitForControl(node->ifTrue()); + ControlStatus falseBranch = ControlStatus(false, Returns_Never); + + if (node->ifFalse()) + falseBranch = visitForControl(node->ifFalse()); + + control_->merge(trueBranch, falseBranch); +} + +void +SemanticAnalysis::visit(IndexExpression *node) +{ + Local left(zone_, visitForLoad(node->left())); + Local right(zone_, visitForLoad(node->right())); + + if (!left || !right) + return; + + if (!right->isInt32() && !right->isEnum()) { + cc_.reportError(node->right()->pos(), Message_IndexMustBeInteger); + return; + } + if (!left->isArray()) { + cc_.reportError(node->left()->pos(), Message_IndexBaseMustBeArray); + return; + } + + // Note: char arrays are automatically up-casted to return an int32. + if (left->isCharArray()) + node->setType(zone_->types()->getPrimitive(PrimitiveType_Int32)); + else + node->setType(left->contained()); +} + +void +SemanticAnalysis::visit(FieldExpression *node) +{ + Local left(zone_, visitForLoad(node->left())); + if (!left) + return; + + if (!left->isStruct()) { + cc_.reportError(node->pos(), Message_PropertyNotFound, node->field()->chars(), + GetTypeName(left)); + return; + } + + Local field(zone_, node->field()); + + unsigned offset; + Local desc(zone_, left->lookupField(field, &offset)); + if (!desc) { + cc_.reportError(node->pos(), Message_PropertyNotFound, node->field()->chars(), + GetTypeName(left)); + return; + } + + node->setFieldIndex(offset); + node->setType(desc->type()); +} + +void +SemanticAnalysis::visit(EnumStatement *node) +{ + Local name(zone_, node->name()); + Local type(zone_); + if (name) + type = zone_->types()->newEnum(name); + else + type = zone_->types()->getPrimitive(PrimitiveType_Int32); + + if (!type) + return; + + if (node->decl()) + node->decl()->setType(type); + + int value = 0; + for (size_t i = 0; i < node->entries()->length(); i++) { + EnumStatement::Entry &entry = node->entries()->at(i); + + BoxedPrimitive box = BoxedPrimitive::Int(value); + if (entry.expr) { + entry.expr->accept(this); + if (!EvaluateForConstant(entry.expr, &box) || box.type() != PrimitiveType_Int32) { + cc_.reportError(entry.expr->pos(), Message_EnumFieldNotConstant); + return; + } + + assert(box.type() == PrimitiveType_Int32); + value = box.toInt(); + } + +#if 0 + entry.name->set(type, box); +#endif + value++; + } +} + +static int +SortCaseIntValues(const void *p1, const void *p2) +{ + CaseValue *v1 = (CaseValue *)p1; + CaseValue *v2 = (CaseValue *)p2; + + return v1->box.toInt() - v2->box.toInt(); +} + +void +SemanticAnalysis::visit(SwitchStatement *node) +{ + Local caseType(zone_, visitForLoad(node->expression())); + if (!caseType) + return; + + if (!caseType->isEnum() && !caseType->isInt32()) { + cc_.reportError(node->pos(), Message_CaseMustBeInteger); + return; + } + + PoolList *values = new (zone_->pool()) PoolList(); + ControlStatus status = ControlStatus(false, Returns_Never); + + Local check(zone_); + for (size_t i = 0; i < node->cases()->length(); i++) { + Case *c = node->cases()->at(i); + + check = visitForLoad(c->expression()); + if (!check) + return; + + if (!typeCheckAssignment(c->expression()->pos(), check, caseType, false)) + return; + + CaseValue v(i); + if (!EvaluateForConstant(c->expression(), &v.box)) { + cc_.reportError(c->expression()->pos(), Message_CaseMustBeConstant); + return; + } + if (!values->append(v)) + return; + + if (c->others()) { + for (size_t j = 0; j < c->others()->length(); j++) { + Expression *expr = c->others()->at(j); + check = visitForLoad(expr); + if (!check) + return; + + if (!typeCheckAssignment(expr->pos(), check, caseType, false)) + return; + + CaseValue v(i); + if (!EvaluateForConstant(expr, &v.box)) { + cc_.reportError(expr->pos(), Message_CaseMustBeConstant); + return; + } + if (!values->append(v)) + return; + } + } + + ControlStatus latest = visitForControl(c->statement()); + + if (i != 0) + control_->merge(status, latest); + + status = latest; + } + + if (node->defaultCase()) { + ControlStatus latest = visitForControl(node->defaultCase()); + + if (node->cases()->length()) { + // If we had any other cases, we must merge. + control_->merge(status, latest); + } else { + // Otherwise, control-flow always flows through here. + control_->inherit(latest); + } + } else if (node->cases()->length()) { + control_->merge(status, ControlStatus(false, Returns_Never)); + } + + if (values->length() > 1) { + qsort(values->buffer(), values->length(), sizeof(CaseValue), SortCaseIntValues); + + for (size_t i = 1; i < values->length(); i++) { + CaseValue &prev = values->at(i - 1); + CaseValue &cur = values->at(i); + if (prev.box.toInt() == cur.box.toInt()) { + cc_.reportError(node->cases()->at(cur.statement)->expression()->pos(), Message_CaseHasSameValue); + return; + } + } + } + + node->setCaseValueList(values); +} + +void +SemanticAnalysis::visit(StructureStatement *node) +{ +#if 0 + // If this node already has a type, it was pre-analyzed, so skip it. + if (node->decl()->type()) + return; + + FieldList *fields = node->fields(); + Local desctable(zone_, FixedArray::New(zone_, fields->length(), Heap::Tenure_Old)); + if (!desctable) + return; + + Vector composedList; + StructureBufferBuilder builder; + + Local type(zone_); + Local name(zone_); + Local desc(zone_); + for (size_t i = 0; i < fields->length(); i++) { + StructureStatement::Field *field = fields->at(i); + + type = bindType(field->pos(), field->type()); + if (!type) + return; + + if (field->dimensions()) { + type = buildArrayType(type, field->dimensions(), false); + if (!type) + return; + + if (!ArrayMap::Attach(zone_, type)) + return; + } + + if (IsScalarString(type)) { + cc_.reportError(field->pos(), Message_CannotDeclareStringCell); + return; + } + + unsigned offset; + if (!builder.add(type, &offset)) + return; + + name = field->name(); + desc = Descriptor::New(zone_, name, type, offset); + if (!desc) + return; + + desctable->set(zone_, i, desc); + + if (type->isStruct() || type->isArray()) { + if (!composedList.append(i)) + return; + } + } + + Local composed(zone_); + if (composedList.length()) { + composed = FixedArray::New(zone_, composedList.length(), Heap::Tenure_Old); + if (!composed) + return; + + for (unsigned i = 0; i < composedList.length(); i++) + composed->set(zone_, i, desctable->at(i)); + } + + Local map(zone_, StructMap::New(zone_, builder, composed)); + if (!map) + return; + + name = node->decl()->name(); + type = zone_->types()->newStruct(name, desctable, map); + if (!type) + return; + + map->setType(type); + node->decl()->setType(type); +#endif +} + +void +SemanticAnalysis::visit(StringLiteral *node) +{ + Local type(zone_, zone_->types()->getPrimitive(PrimitiveType_Char)); + if (!type) + return; + + type = Type::NewArray(zone_, type, node->string()->length() + 1, true); + if (!type) + return; + + node->setType(type); +} + +void +SemanticAnalysis::visit(FloatLiteral *node) +{ + Local type(zone_, zone_->types()->getPrimitive(PrimitiveType_Float)); + if (!type) + return; + + node->setType(type); +} + +void +SemanticAnalysis::visit(BreakStatement *node) +{ + if (!loop_) { + cc_.reportError(node->pos(), Message_BreakOutsideLoop); + return; + } + + // Note: ignore breaks that occur in unreachable control-flow. + if (!control_->status().terminates()) + loop_->addBreak(); + + control_->terminate(Returns_Never); +} + +void +SemanticAnalysis::visit(ContinueStatement *node) +{ + if (!loop_) { + cc_.reportError(node->pos(), Message_ContinueOutsideLoop); + return; + } + + // Note: ignore continues that occur in unreachable control-flow. + if (!control_->status().terminates()) + loop_->addContinue(); + + control_->terminate(Returns_Never); +} + +void +SemanticAnalysis::visit(ArrayLiteral *node) +{ + // We should never visit this. + cc_.reportError(node->pos(), Message_UnexpectedArrayLiteral); +} + +void +SemanticAnalysis::visit(ImportStatement *node) +{ +} + #endif \ No newline at end of file diff -r c4b1341297e5 -r ba85a47ee414 src/OldSema.h --- a/src/OldSema.h Mon Jun 10 00:46:29 2013 +0100 +++ b/src/OldSema.h Mon Jun 10 01:04:43 2013 +0100 @@ -1,237 +1,237 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of JITCraft. - * - * JITCraft is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * JITCraft. If not, see http://www.gnu.org/licenses/. - */ -#ifndef _include_semantic_analysis_h_ -#define _include_semantic_analysis_h_ - -#include -#include "AST.h" -#include "../Types.h" - -namespace ke { - -class Box; - -class SemanticAnalysis : public AstVisitor -{ - Zone *zone_; - CompileContext &cc_; - ParseTree *tree_; - Scope *scope_; - NamedFunction *function_; - - enum ReturnStatus - { - Returns_Never, - Returns_Sometimes, - Returns_Always - }; - - // Tuple that describes whether a path terminates control flow, and - // if it is guaranteed to end in a return. - class ControlStatus - { - bool terminates_; - ReturnStatus returns_; - - public: - ControlStatus(bool terminates, ReturnStatus returns) - : terminates_(terminates), - returns_(returns) - { - } - - bool terminates() const { - return terminates_; - } - ReturnStatus returns() const { - return returns_; - } - }; - - // Used to keep track of terminating control flow and return statements - // inside control structures. - class ControlScope - { - ReturnStatus returns_; - ControlScope **prevp_; - ControlScope *prev_; - bool terminates_; - - public: - ControlScope(ControlScope **prev) - : returns_(Returns_Never), - prevp_(prev), - prev_(*prev), - terminates_(false) - { - *prevp_ = this; - } - - ControlStatus status() const { - return ControlStatus(terminates_, returns_); - } - - // We allow multiple terminators, but only the first one takes effect. - // For example, |break; continue;| is legal but the continue is - // unreachable. - void returns(ReturnStatus status) { - if (terminates_) - return; - if (status > returns_) - returns_ = status; - } - void terminate(ReturnStatus status) { - if (terminates_) - return; - terminates_ = true; - returns_ = status; - } - void merge(const ControlStatus &left, const ControlStatus &right); - void inherit(const ControlStatus &other); - - ~ControlScope() - { - *prevp_ = prev_; - } - }; - - class LoopScope - { - LoopScope **prevp_; - LoopScope *prev_; - unsigned breaks_; - unsigned continues_; - - public: - LoopScope(LoopScope **prev) - : prevp_(prev), - prev_(*prev), - breaks_(0), - continues_(0) - { - *prevp_ = this; - } - - ~LoopScope() - { - *prevp_ = prev_; - } - - void addBreak() { - breaks_++; - } - void addContinue() { - continues_++; - } - unsigned breaks() const { - return breaks_; - } - unsigned continues() const { - return continues_; - } - }; - - ControlScope *control_; - LoopScope *loop_; - - private: - bool canTest(AstNode *node, Handle type); - void typeMismatch(const SourcePosition &pos, Handle actual, Handle given); - void unaryTypeError(AstNode *node, TokenKind token, Handle type); - void binaryTypeError(AstNode *node, TokenKind token, Handle left, Handle right); - Type *typeCheckBinary(AstNode *node, TokenKind token, Handle left, Handle right); - - Type *bindType(const SourcePosition &pos, BoundName *name); - - // These check if one type is compatible with another type, for various - // purposes, like: - // return (expr) matches function return type - // call(a, b) matches expected arg type - // a = b matches declared type - // - bool typeCheckArrayAssignment(const SourcePosition &pos, Handle given, Handle actual, bool forAssign); - bool typeCheckAssignment(const SourcePosition &pos, Handle given, Handle actual, bool forAssign); - bool typeCheckParameter(AstNode *node, Handle given, Handle actual); - - Type *visitForLoad(Expression *expression); - bool preanalyze(FunctionStatement *node); - Type *visitForLValue(Expression *expression); - Type *visitAndApplyType(Expression *expression); - ControlStatus visitForControl(Statement *statement); - - bool evaluateDimensions(ExpressionList *exprs, int dims[MAX_ARRAY_DEPTH]); - bool inferFixedArrayLiteral(ArrayLiteral *literal, - Handle type, - int dims[MAX_ARRAY_DEPTH], - unsigned dimcount, - unsigned level); - - // Builds an array type used for parameters, returns, or field entries. - Type *buildArrayType(Handle type, ExpressionList *dimensions, bool isConst); - - Box *evaluateDefaultParameter(Handle type, Parameter *parameter); - bool checkArgumentCount(Handle type, unsigned actual); - bool buildFunctionArgs(AstNode *node, TokenKind kind, const FunctionSignature &sig, - FixedArray **args, FixedArray **defaults); - - void markCallResultUsed(CallExpression *call); - - bool determineLiveFunctions(); - - public: - SemanticAnalysis(Zone *zone, CompileContext &cc, ParseTree *tree); - bool analyze(); - - public: - void visit(NameProxy *name); - void visit(Assignment *node); - void visit(BinaryExpression *node); - void visit(ReturnStatement *node); - void visit(ForStatement *node); - void visit(BlockStatement *node); - void visit(VariableDeclaration *node); - void visit(IntegerLiteral *node); - void visit(ExpressionStatement *node); - void visit(FunctionStatement *node); - void visit(CallExpression *node); - void visit(IfStatement *node); - void visit(IndexExpression *node); - void visit(EnumStatement *node); - void visit(FloatLiteral *node); - void visit(WhileStatement *node); - void visit(BreakStatement *node); - void visit(ContinueStatement *node); - void visit(StringLiteral *node); - void visit(IncDecExpression *node); - void visit(UnaryExpression *node); - void visit(TernaryExpression *node); - void visit(BooleanLiteral *node); - void visit(StructureStatement *node); - void visit(FieldExpression *node); - void visit(SwitchStatement *node); - void visit(FunctionTypeStatement *node); - void visit(ArrayLiteral *node); - void visit(ImportStatement *node); -}; - -const char *GetTypeName(Type *type); - -} // namespace ke - -#endif // _include_semantic_analysis_h_ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_semantic_analysis_h_ +#define _include_semantic_analysis_h_ + +#include +#include "AST.h" +#include "../Types.h" + +namespace ke { + +class Box; + +class SemanticAnalysis : public AstVisitor +{ + Zone *zone_; + CompileContext &cc_; + ParseTree *tree_; + Scope *scope_; + NamedFunction *function_; + + enum ReturnStatus + { + Returns_Never, + Returns_Sometimes, + Returns_Always + }; + + // Tuple that describes whether a path terminates control flow, and + // if it is guaranteed to end in a return. + class ControlStatus + { + bool terminates_; + ReturnStatus returns_; + + public: + ControlStatus(bool terminates, ReturnStatus returns) + : terminates_(terminates), + returns_(returns) + { + } + + bool terminates() const { + return terminates_; + } + ReturnStatus returns() const { + return returns_; + } + }; + + // Used to keep track of terminating control flow and return statements + // inside control structures. + class ControlScope + { + ReturnStatus returns_; + ControlScope **prevp_; + ControlScope *prev_; + bool terminates_; + + public: + ControlScope(ControlScope **prev) + : returns_(Returns_Never), + prevp_(prev), + prev_(*prev), + terminates_(false) + { + *prevp_ = this; + } + + ControlStatus status() const { + return ControlStatus(terminates_, returns_); + } + + // We allow multiple terminators, but only the first one takes effect. + // For example, |break; continue;| is legal but the continue is + // unreachable. + void returns(ReturnStatus status) { + if (terminates_) + return; + if (status > returns_) + returns_ = status; + } + void terminate(ReturnStatus status) { + if (terminates_) + return; + terminates_ = true; + returns_ = status; + } + void merge(const ControlStatus &left, const ControlStatus &right); + void inherit(const ControlStatus &other); + + ~ControlScope() + { + *prevp_ = prev_; + } + }; + + class LoopScope + { + LoopScope **prevp_; + LoopScope *prev_; + unsigned breaks_; + unsigned continues_; + + public: + LoopScope(LoopScope **prev) + : prevp_(prev), + prev_(*prev), + breaks_(0), + continues_(0) + { + *prevp_ = this; + } + + ~LoopScope() + { + *prevp_ = prev_; + } + + void addBreak() { + breaks_++; + } + void addContinue() { + continues_++; + } + unsigned breaks() const { + return breaks_; + } + unsigned continues() const { + return continues_; + } + }; + + ControlScope *control_; + LoopScope *loop_; + + private: + bool canTest(AstNode *node, Handle type); + void typeMismatch(const SourcePosition &pos, Handle actual, Handle given); + void unaryTypeError(AstNode *node, TokenKind token, Handle type); + void binaryTypeError(AstNode *node, TokenKind token, Handle left, Handle right); + Type *typeCheckBinary(AstNode *node, TokenKind token, Handle left, Handle right); + + Type *bindType(const SourcePosition &pos, BoundName *name); + + // These check if one type is compatible with another type, for various + // purposes, like: + // return (expr) matches function return type + // call(a, b) matches expected arg type + // a = b matches declared type + // + bool typeCheckArrayAssignment(const SourcePosition &pos, Handle given, Handle actual, bool forAssign); + bool typeCheckAssignment(const SourcePosition &pos, Handle given, Handle actual, bool forAssign); + bool typeCheckParameter(AstNode *node, Handle given, Handle actual); + + Type *visitForLoad(Expression *expression); + bool preanalyze(FunctionStatement *node); + Type *visitForLValue(Expression *expression); + Type *visitAndApplyType(Expression *expression); + ControlStatus visitForControl(Statement *statement); + + bool evaluateDimensions(ExpressionList *exprs, int dims[MAX_ARRAY_DEPTH]); + bool inferFixedArrayLiteral(ArrayLiteral *literal, + Handle type, + int dims[MAX_ARRAY_DEPTH], + unsigned dimcount, + unsigned level); + + // Builds an array type used for parameters, returns, or field entries. + Type *buildArrayType(Handle type, ExpressionList *dimensions, bool isConst); + + Box *evaluateDefaultParameter(Handle type, Parameter *parameter); + bool checkArgumentCount(Handle type, unsigned actual); + bool buildFunctionArgs(AstNode *node, TokenKind kind, const FunctionSignature &sig, + FixedArray **args, FixedArray **defaults); + + void markCallResultUsed(CallExpression *call); + + bool determineLiveFunctions(); + + public: + SemanticAnalysis(Zone *zone, CompileContext &cc, ParseTree *tree); + bool analyze(); + + public: + void visit(NameProxy *name); + void visit(Assignment *node); + void visit(BinaryExpression *node); + void visit(ReturnStatement *node); + void visit(ForStatement *node); + void visit(BlockStatement *node); + void visit(VariableDeclaration *node); + void visit(IntegerLiteral *node); + void visit(ExpressionStatement *node); + void visit(FunctionStatement *node); + void visit(CallExpression *node); + void visit(IfStatement *node); + void visit(IndexExpression *node); + void visit(EnumStatement *node); + void visit(FloatLiteral *node); + void visit(WhileStatement *node); + void visit(BreakStatement *node); + void visit(ContinueStatement *node); + void visit(StringLiteral *node); + void visit(IncDecExpression *node); + void visit(UnaryExpression *node); + void visit(TernaryExpression *node); + void visit(BooleanLiteral *node); + void visit(StructureStatement *node); + void visit(FieldExpression *node); + void visit(SwitchStatement *node); + void visit(FunctionTypeStatement *node); + void visit(ArrayLiteral *node); + void visit(ImportStatement *node); +}; + +const char *GetTypeName(Type *type); + +} // namespace ke + +#endif // _include_semantic_analysis_h_ diff -r c4b1341297e5 -r ba85a47ee414 src/Opcodes.cpp --- a/src/Opcodes.cpp Mon Jun 10 00:46:29 2013 +0100 +++ b/src/Opcodes.cpp Mon Jun 10 01:04:43 2013 +0100 @@ -1,350 +1,350 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of JITCraft. - * - * JITCraft is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * JITCraft. If not, see http://www.gnu.org/licenses/. - */ -#include -#include "Opcodes.h" -#include "Utility.h" -#include "Code.h" -#include "Zone.h" -#include "Array.h" -#include "Strings.h" -#include "CompactBuffer.h" -#include "Structures.h" -#include "Modules.h" -#include "Heap-inl.h" - -using namespace ke; - -OpInfo ke::OpcodeInfo[] = -{ -#define OPDEF(name, text, length, uses, defs) \ - {text, length, uses, defs}, -#include "Opcodes.tbl" -#undef OPDEF -}; - -Code * -Code::New(Zone *zone, Handle bytecode, Handle locals, - Handle objects, Handle strings, unsigned maxStackDepth, - unsigned maxLoopDepth, Handle safepoints, Handle sourcemap) -{ - Local code(zone, Code::cast(zone->allocate(MapKind_Code, sizeof(Code), Heap::Tenure_Old))); - if (!code) - return NULL; - - code->bytecode_ = bytecode; - code->locals_ = locals; - code->objects_ = objects; - code->maxStackDepth_ = maxStackDepth; - code->module_ = NULL; - code->safepoints_ = safepoints; - code->sourcemap_ = sourcemap; - code->lineNumber_ = 0; - code->strings_ = strings; - code->maxLoopDepth_ = maxLoopDepth; - return code; -} - -static int -CompareSafepoints(const void *a, const void *b) -{ - const unsigned *sa = reinterpret_cast(a); - const unsigned *sb = reinterpret_cast(b); - if (*sa > *sb) - return 1; - if (*sa == *sb) - return 0; - return -1; -} - -uint8 * -Code::safepointAt(unsigned pcOffset) -{ - // Only specific opcodes may have safepoints. Asking for a safepoint at - // any other position indicates a failure to mark that position as a - // safepoint, which is a bug. -#ifndef NDEBUG - Opcode op = Opcode(*(start() + pcOffset)); - assert(op == OP_CALL || - op == OP_NEWFIXED || - op == OP_NEWEMPTY || - op == OP_NEWSIZED || - op == OP_VARREF_LIFO || - op == OP_ARGREF_LIFO || - op == OP_SLOTREF_LIFO || - op == OP_COPYARRAY || - op == OP_CALLNATIVE || - op == OP_PUBLIC); -#endif - - if (!safepoints_) - return NULL; - - size_t bytesPerSafepoint = BytesPerSafepoint(maxStackDepth()); - assert(!(safepoints_->length() % bytesPerSafepoint)); - - size_t count = safepoints_->length() / bytesPerSafepoint; - - void *result = bsearch(&pcOffset, - safepoints_->raw(), - count, - bytesPerSafepoint, - CompareSafepoints); - - if (!result) - return NULL; - - assert(*reinterpret_cast(result) == pcOffset); - - return Address(result) + sizeof(unsigned); -} - -SourcePosition -Code::pcToSource(unsigned pc) -{ - assert(pc < bytecode_->length()); - - CompactBufferReader reader(sourcemap_->raw(), sourcemap_->raw() + sourcemap_->length()); - - unsigned line = 1; - unsigned col = 0; - unsigned curPc = 0; - while (!reader.done()) { - curPc += reader.readUint32(); - if (curPc > pc) - break; - - line = reader.readUint32(); - col = reader.readUint32(); - } - - return SourcePosition(lineNumber_ + line, col); -} - -// All jumps must be the same length. -STATIC_ASSERT(OP_JT_LENGTH == OP_JUMP_LENGTH); -STATIC_ASSERT(OP_JF_LENGTH == OP_JUMP_LENGTH); - -void -ke::Disassemble(Code *code, FILE *fp) -{ - bytecode *pc = code->start(); - for (;;) { - Opcode op = Opcode(*pc); - - fprintf(fp, "%-4d %s ", int(pc - code->start()), OpcodeInfo[op].name); - - switch (op) { - case OP_NOP: - case OP_RETURN: - case OP_RETURNVOID: - case OP_ADD: - case OP_SUB: - case OP_MUL: - case OP_DIV: - case OP_ADD_F: - case OP_SUB_F: - case OP_MUL_F: - case OP_DIV_F: - case OP_CVT_I2F: - case OP_CVT_I2B: - case OP_POP: - case OP_GETELEM: - case OP_SETELEM: - case OP_SHL: - case OP_SHR: - case OP_USHR: - case OP_BITAND: - case OP_BITOR: - case OP_BITXOR: - case OP_GETREF_I: - case OP_SETREF_I: - case OP_GETREF_F: - case OP_SETREF_F: - case OP_SLOTREF_LIFO: - case OP_COPYARRAY: - case OP_COPYSTRUCT: - case OP_DUP: - case OP_DUP2: - case OP_SWAP: - case OP_ROLL3: - case OP_PICK2: - case OP_PICK3: - case OP_CALL: - case OP_LT: - case OP_LE: - case OP_GE: - case OP_GT: - case OP_EQ: - case OP_NE: - case OP_LT_F: - case OP_LE_F: - case OP_GE_F: - case OP_GT_F: - case OP_EQ_F: - case OP_NE_F: - case OP_BITNOT: - case OP_NOT: - case OP_NOT_F: - case OP_NEG: - case OP_NEG_F: - case OP_MOD: - case OP_BITCAST: - case OP_TRUE: - case OP_FALSE: - case OP_SIZEOF: - case OP_PUBLIC: - case OP_LEAVELOOP: - case OP_DUPARRAY: - case OP_DEPARRAY: - break; - - case OP_ENTERLOOP: - if (pc[1]) - fprintf(fp, "; lifo region"); - break; - - case OP_STOP: - fprintf(fp, "\n"); - break; - - case OP_INT: - fprintf(fp, "%d", READ_INT32(pc)); - break; - - case OP_FLOAT: - fprintf(fp, "%f", READ_FLOAT(pc)); - break; - - case OP_JT: - case OP_JF: - case OP_JUMP: - case OP_CONTINUE: - case OP_BREAK: - fprintf(fp, "%u (%+d)", unsigned((pc + READ_INT32(pc)) - code->start()), READ_INT32(pc)); - break; - - case OP_NEWFIXED: - case OP_NEWEMPTY: - case OP_NEWSIZED: - { - Heap::Tenure tenure = (Heap::Tenure)pc[1]; - //ArrayMap *map = ArrayMap::cast(code->map(READ_UINT32(pc + 1))); - if (tenure == Heap::Tenure_Stack) - fprintf(fp, " ; lifo"); - else if (tenure == Heap::Tenure_Old) - fprintf(fp, " ; pretenured"); - else if (tenure == Heap::Tenure_Default) - fprintf(fp, " ; newborn"); - break; - } - - case OP_NEWSTRUCT: - { - Heap::Tenure tenure = (Heap::Tenure)pc[1]; - StructMap *map = StructMap::cast(code->map(READ_UINT32(pc + 1))); - fprintf(fp, "%s", StructType::cast(map->type())->name()->chars()); - if (tenure == Heap::Tenure_Stack) - fprintf(fp, " ; lifo"); - else if (tenure == Heap::Tenure_Old) - fprintf(fp, " ; pretenured"); - else if (tenure == Heap::Tenure_Default) - fprintf(fp, " ; newborn"); - break; - } - - case OP_GETLOCAL: - case OP_SETLOCAL: - case OP_GETARG: - case OP_SETARG: - case OP_SAVELIFO: - case OP_UNWINDLIFO: - case OP_VARREF_LIFO: - case OP_ARGREF_LIFO: - case OP_OBJECT: - case OP_GETFIELD: - case OP_SETFIELD: - fprintf(fp, "%u", READ_UINT32(pc)); - break; - - case OP_CALLNATIVE: - { - unsigned argc = READ_UINT32(pc); - fprintf(fp, " ; %u args", argc); - break; - } - - case OP_TABLESWITCH: - { - int low = READ_TABLESWITCH_LOW(pc); - int high = READ_TABLESWITCH_HIGH(pc); - fprintf(fp, "%d, %d\n", low, high); - break; - } - - case OP_GETGLOBAL: - case OP_SETGLOBAL: - { - unsigned index = READ_UINT32(pc); - fprintf(fp, "%s", String::cast(code->strings()->at(index))->chars()); - break; - } - - case OP_IMPORT: - { - unsigned index = READ_UINT32(pc); - FixedArray *path = code->module()->importPathAt(index); - fprintf(fp, " ; "); - for (unsigned i = 0; i < path->length(); i++) { - fprintf(fp, "%s", String::cast(path->at(i))->chars()); - if (i != path->length() - 1) - fprintf(fp, "."); - } - break; - } - - default: - // Unknown opcode! - assert(false); - return; - } - - if (op == OP_STOP) - break; - - fprintf(fp, "\n"); - pc += OpcodeInfo[op].length; - assert(pc < code->start() + code->length()); - } - - fprintf(fp, "\nSafepoints:\n"); - ByteArray *safepoints = code->safepoints(); - if (safepoints) { - size_t nsafepoints = safepoints->length() / Code::BytesPerSafepoint(code->maxStackDepth()); - for (size_t i = 0; i < nsafepoints; i++) { - Address address = safepoints->raw() + Code::BytesPerSafepoint(code->maxStackDepth()) * i; - fprintf(fp, "%4d: ", *reinterpret_cast(address)); - unsigned char *bits = (unsigned char *)(address + sizeof(unsigned)); - for (unsigned j = 0; j < code->maxStackDepth(); j++) { - if (bits[j / 8] & (1 << (j % 8))) - fprintf(fp, "%d ", j); - } - fprintf(fp, "\n"); - } - } -} +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include +#include "Opcodes.h" +#include "Utility.h" +#include "Code.h" +#include "Zone.h" +#include "Array.h" +#include "Strings.h" +#include "CompactBuffer.h" +#include "Structures.h" +#include "Modules.h" +#include "Heap-inl.h" + +using namespace ke; + +OpInfo ke::OpcodeInfo[] = +{ +#define OPDEF(name, text, length, uses, defs) \ + {text, length, uses, defs}, +#include "Opcodes.tbl" +#undef OPDEF +}; + +Code * +Code::New(Zone *zone, Handle bytecode, Handle locals, + Handle objects, Handle strings, unsigned maxStackDepth, + unsigned maxLoopDepth, Handle safepoints, Handle sourcemap) +{ + Local code(zone, Code::cast(zone->allocate(MapKind_Code, sizeof(Code), Heap::Tenure_Old))); + if (!code) + return NULL; + + code->bytecode_ = bytecode; + code->locals_ = locals; + code->objects_ = objects; + code->maxStackDepth_ = maxStackDepth; + code->module_ = NULL; + code->safepoints_ = safepoints; + code->sourcemap_ = sourcemap; + code->lineNumber_ = 0; + code->strings_ = strings; + code->maxLoopDepth_ = maxLoopDepth; + return code; +} + +static int +CompareSafepoints(const void *a, const void *b) +{ + const unsigned *sa = reinterpret_cast(a); + const unsigned *sb = reinterpret_cast(b); + if (*sa > *sb) + return 1; + if (*sa == *sb) + return 0; + return -1; +} + +uint8 * +Code::safepointAt(unsigned pcOffset) +{ + // Only specific opcodes may have safepoints. Asking for a safepoint at + // any other position indicates a failure to mark that position as a + // safepoint, which is a bug. +#ifndef NDEBUG + Opcode op = Opcode(*(start() + pcOffset)); + assert(op == OP_CALL || + op == OP_NEWFIXED || + op == OP_NEWEMPTY || + op == OP_NEWSIZED || + op == OP_VARREF_LIFO || + op == OP_ARGREF_LIFO || + op == OP_SLOTREF_LIFO || + op == OP_COPYARRAY || + op == OP_CALLNATIVE || + op == OP_PUBLIC); +#endif + + if (!safepoints_) + return NULL; + + size_t bytesPerSafepoint = BytesPerSafepoint(maxStackDepth()); + assert(!(safepoints_->length() % bytesPerSafepoint)); + + size_t count = safepoints_->length() / bytesPerSafepoint; + + void *result = bsearch(&pcOffset, + safepoints_->raw(), + count, + bytesPerSafepoint, + CompareSafepoints); + + if (!result) + return NULL; + + assert(*reinterpret_cast(result) == pcOffset); + + return Address(result) + sizeof(unsigned); +} + +SourcePosition +Code::pcToSource(unsigned pc) +{ + assert(pc < bytecode_->length()); + + CompactBufferReader reader(sourcemap_->raw(), sourcemap_->raw() + sourcemap_->length()); + + unsigned line = 1; + unsigned col = 0; + unsigned curPc = 0; + while (!reader.done()) { + curPc += reader.readUint32(); + if (curPc > pc) + break; + + line = reader.readUint32(); + col = reader.readUint32(); + } + + return SourcePosition(lineNumber_ + line, col); +} + +// All jumps must be the same length. +STATIC_ASSERT(OP_JT_LENGTH == OP_JUMP_LENGTH); +STATIC_ASSERT(OP_JF_LENGTH == OP_JUMP_LENGTH); + +void +ke::Disassemble(Code *code, FILE *fp) +{ + bytecode *pc = code->start(); + for (;;) { + Opcode op = Opcode(*pc); + + fprintf(fp, "%-4d %s ", int(pc - code->start()), OpcodeInfo[op].name); + + switch (op) { + case OP_NOP: + case OP_RETURN: + case OP_RETURNVOID: + case OP_ADD: + case OP_SUB: + case OP_MUL: + case OP_DIV: + case OP_ADD_F: + case OP_SUB_F: + case OP_MUL_F: + case OP_DIV_F: + case OP_CVT_I2F: + case OP_CVT_I2B: + case OP_POP: + case OP_GETELEM: + case OP_SETELEM: + case OP_SHL: + case OP_SHR: + case OP_USHR: + case OP_BITAND: + case OP_BITOR: + case OP_BITXOR: + case OP_GETREF_I: + case OP_SETREF_I: + case OP_GETREF_F: + case OP_SETREF_F: + case OP_SLOTREF_LIFO: + case OP_COPYARRAY: + case OP_COPYSTRUCT: + case OP_DUP: + case OP_DUP2: + case OP_SWAP: + case OP_ROLL3: + case OP_PICK2: + case OP_PICK3: + case OP_CALL: + case OP_LT: + case OP_LE: + case OP_GE: + case OP_GT: + case OP_EQ: + case OP_NE: + case OP_LT_F: + case OP_LE_F: + case OP_GE_F: + case OP_GT_F: + case OP_EQ_F: + case OP_NE_F: + case OP_BITNOT: + case OP_NOT: + case OP_NOT_F: + case OP_NEG: + case OP_NEG_F: + case OP_MOD: + case OP_BITCAST: + case OP_TRUE: + case OP_FALSE: + case OP_SIZEOF: + case OP_PUBLIC: + case OP_LEAVELOOP: + case OP_DUPARRAY: + case OP_DEPARRAY: + break; + + case OP_ENTERLOOP: + if (pc[1]) + fprintf(fp, "; lifo region"); + break; + + case OP_STOP: + fprintf(fp, "\n"); + break; + + case OP_INT: + fprintf(fp, "%d", READ_INT32(pc)); + break; + + case OP_FLOAT: + fprintf(fp, "%f", READ_FLOAT(pc)); + break; + + case OP_JT: + case OP_JF: + case OP_JUMP: + case OP_CONTINUE: + case OP_BREAK: + fprintf(fp, "%u (%+d)", unsigned((pc + READ_INT32(pc)) - code->start()), READ_INT32(pc)); + break; + + case OP_NEWFIXED: + case OP_NEWEMPTY: + case OP_NEWSIZED: + { + Heap::Tenure tenure = (Heap::Tenure)pc[1]; + //ArrayMap *map = ArrayMap::cast(code->map(READ_UINT32(pc + 1))); + if (tenure == Heap::Tenure_Stack) + fprintf(fp, " ; lifo"); + else if (tenure == Heap::Tenure_Old) + fprintf(fp, " ; pretenured"); + else if (tenure == Heap::Tenure_Default) + fprintf(fp, " ; newborn"); + break; + } + + case OP_NEWSTRUCT: + { + Heap::Tenure tenure = (Heap::Tenure)pc[1]; + StructMap *map = StructMap::cast(code->map(READ_UINT32(pc + 1))); + fprintf(fp, "%s", StructType::cast(map->type())->name()->chars()); + if (tenure == Heap::Tenure_Stack) + fprintf(fp, " ; lifo"); + else if (tenure == Heap::Tenure_Old) + fprintf(fp, " ; pretenured"); + else if (tenure == Heap::Tenure_Default) + fprintf(fp, " ; newborn"); + break; + } + + case OP_GETLOCAL: + case OP_SETLOCAL: + case OP_GETARG: + case OP_SETARG: + case OP_SAVELIFO: + case OP_UNWINDLIFO: + case OP_VARREF_LIFO: + case OP_ARGREF_LIFO: + case OP_OBJECT: + case OP_GETFIELD: + case OP_SETFIELD: + fprintf(fp, "%u", READ_UINT32(pc)); + break; + + case OP_CALLNATIVE: + { + unsigned argc = READ_UINT32(pc); + fprintf(fp, " ; %u args", argc); + break; + } + + case OP_TABLESWITCH: + { + int low = READ_TABLESWITCH_LOW(pc); + int high = READ_TABLESWITCH_HIGH(pc); + fprintf(fp, "%d, %d\n", low, high); + break; + } + + case OP_GETGLOBAL: + case OP_SETGLOBAL: + { + unsigned index = READ_UINT32(pc); + fprintf(fp, "%s", String::cast(code->strings()->at(index))->chars()); + break; + } + + case OP_IMPORT: + { + unsigned index = READ_UINT32(pc); + FixedArray *path = code->module()->importPathAt(index); + fprintf(fp, " ; "); + for (unsigned i = 0; i < path->length(); i++) { + fprintf(fp, "%s", String::cast(path->at(i))->chars()); + if (i != path->length() - 1) + fprintf(fp, "."); + } + break; + } + + default: + // Unknown opcode! + assert(false); + return; + } + + if (op == OP_STOP) + break; + + fprintf(fp, "\n"); + pc += OpcodeInfo[op].length; + assert(pc < code->start() + code->length()); + } + + fprintf(fp, "\nSafepoints:\n"); + ByteArray *safepoints = code->safepoints(); + if (safepoints) { + size_t nsafepoints = safepoints->length() / Code::BytesPerSafepoint(code->maxStackDepth()); + for (size_t i = 0; i < nsafepoints; i++) { + Address address = safepoints->raw() + Code::BytesPerSafepoint(code->maxStackDepth()) * i; + fprintf(fp, "%4d: ", *reinterpret_cast(address)); + unsigned char *bits = (unsigned char *)(address + sizeof(unsigned)); + for (unsigned j = 0; j < code->maxStackDepth(); j++) { + if (bits[j / 8] & (1 << (j % 8))) + fprintf(fp, "%d ", j); + } + fprintf(fp, "\n"); + } + } +} diff -r c4b1341297e5 -r ba85a47ee414 src/Opcodes.h --- a/src/Opcodes.h Mon Jun 10 00:46:29 2013 +0100 +++ b/src/Opcodes.h Mon Jun 10 01:04:43 2013 +0100 @@ -1,113 +1,113 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#ifndef _include_opcodes_h_ -#define _include_opcodes_h_ - -#include -#include - -namespace ke { - -struct SourcePosition -{ - int line; - int col; - - SourcePosition() - : line(-1), - col(-1) - { - } - - SourcePosition(unsigned line, unsigned col) - : line(line), - col(col) - { - } -}; - -enum Opcode -{ -#define OPDEF(name, text, length, uses, defs) \ - name, -#include "Opcodes.tbl" -#undef OPDEF - OPCODES_TOTAL -}; - -#define OPDEF(name, text, length, uses, defs) \ -static const unsigned name##_LENGTH = length; -#include "Opcodes.tbl" -#undef OPDEF - -struct OpInfo -{ - const char *name; - int length; - int nuses; - int ndefs; -}; - -typedef unsigned char bytecode; - -extern OpInfo OpcodeInfo[]; - -static const unsigned MAX_TABLESWITCH_ENTRIES = 256; - -static inline unsigned -TableSwitchEntries(int low, int high) -{ - return unsigned(high - low) + 1; -} - -static inline int -READ_INT32(bytecode *pc) -{ - return *reinterpret_cast(pc + 1); -} -static inline unsigned -READ_UINT32(bytecode *pc) -{ - return *reinterpret_cast(pc + 1); -} -static inline float -READ_FLOAT(bytecode *pc) -{ - return *reinterpret_cast(pc + 1); -} - -static inline int -READ_TABLESWITCH_LOW(bytecode *pc) -{ - return READ_INT32(pc); -} - -static inline int -READ_TABLESWITCH_HIGH(bytecode *pc) -{ - return READ_INT32(pc + sizeof(int)); -} - -class Code; - -void Disassemble(Code *code, FILE *fp); - -} - -#endif // _include_opcodes_h_ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_opcodes_h_ +#define _include_opcodes_h_ + +#include +#include + +namespace ke { + +struct SourcePosition +{ + int line; + int col; + + SourcePosition() + : line(-1), + col(-1) + { + } + + SourcePosition(unsigned line, unsigned col) + : line(line), + col(col) + { + } +}; + +enum Opcode +{ +#define OPDEF(name, text, length, uses, defs) \ + name, +#include "Opcodes.tbl" +#undef OPDEF + OPCODES_TOTAL +}; + +#define OPDEF(name, text, length, uses, defs) \ +static const unsigned name##_LENGTH = length; +#include "Opcodes.tbl" +#undef OPDEF + +struct OpInfo +{ + const char *name; + int length; + int nuses; + int ndefs; +}; + +typedef unsigned char bytecode; + +extern OpInfo OpcodeInfo[]; + +static const unsigned MAX_TABLESWITCH_ENTRIES = 256; + +static inline unsigned +TableSwitchEntries(int low, int high) +{ + return unsigned(high - low) + 1; +} + +static inline int +READ_INT32(bytecode *pc) +{ + return *reinterpret_cast(pc + 1); +} +static inline unsigned +READ_UINT32(bytecode *pc) +{ + return *reinterpret_cast(pc + 1); +} +static inline float +READ_FLOAT(bytecode *pc) +{ + return *reinterpret_cast(pc + 1); +} + +static inline int +READ_TABLESWITCH_LOW(bytecode *pc) +{ + return READ_INT32(pc); +} + +static inline int +READ_TABLESWITCH_HIGH(bytecode *pc) +{ + return READ_INT32(pc + sizeof(int)); +} + +class Code; + +void Disassemble(Code *code, FILE *fp); + +} + +#endif // _include_opcodes_h_ diff -r c4b1341297e5 -r ba85a47ee414 src/Packages.cpp --- a/src/Packages.cpp Mon Jun 10 00:46:29 2013 +0100 +++ b/src/Packages.cpp Mon Jun 10 01:04:43 2013 +0100 @@ -1,226 +1,226 @@ -/* - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#include -#include "Zone.h" -#include "Heap.h" -#include "Strings.h" -#include "Modules.h" -#include "Packages.h" -#include "FileSystem.h" -#include "List.h" -#include "Heap-inl.h" -#include "Handles-inl.h" - -using namespace ke; - -Package * -Package::NewNormalized(Zone *zone, Handle parent, Handle path, Handle name, unsigned flags) -{ - Local package(zone, Package::cast(zone->allocate(MapKind_Package, sizeof(Package), Heap::Tenure_Old))); - if (!package) - return NULL; - - // :TODO: assert path, name are atoms. - - package->flags_ = flags; - package->name_ = name; - package->parent_ = parent; - package->path_ = path; - package->nativeIdentity_ = parent ? parent->getNativeIdentity() : NULL; - - return package; -} - -Package * -Package::New(Zone *zone, Handle parent, const char *path, Handle name, unsigned flags) -{ - Local strpath(zone, zone->makeSymbol(path)); - if (!strpath) - return NULL; - - return Package::NewNormalized(zone, parent, strpath, name, flags); -} - -bool -Package::add(Zone *zone, Handle obj) -{ - Local pkg(zone, this); - - assert(obj->parent() == pkg); - - if (!pkg->contents_) { - pkg->contents_ = List::New(zone); - if (!pkg->contents_) - return false; - } - -#ifndef NDEBUG - // Assert no duplicate names. - for (unsigned i = 0; i < pkg->contents_->length(); i++) - assert(pkg->contents_->at(i)->name() != obj->name()); -#endif - - if (!pkg->contents_->append(zone, obj)) - return false; - - return true; -} - -Importable * -Package::import(Handle name) -{ - if (!contents_) - return NULL; - - for (unsigned i = 0; i < contents_->length(); i++) { - Importable *obj = contents_->at(i); - if (obj->isPackage() && Package::cast(obj)->name() == name) - return obj; - if (obj->isModule() && Module::cast(obj)->name() == name) - return obj; - } - - return NULL; -} - -PackageCache::PackageCache(Zone *zone) - : zone_(zone) -{ -} - -bool -PackageCache::initialize() -{ - return true; -} - -void -PackageCache::accept(VirtualObjectVisitor *visitor) -{ - for (size_t i = 0; i < roots_.length(); i++) - visitor->visit(roots_[i].address()); -} - -bool -PackageCache::checkfs(Handle package, PathComponent *path, Importable **out) -{ - AutoFree left(strdup(package->path()->chars())); - AutoFree right(strdup(path->name->chars())); - AutoFree result; - - if (!JoinPaths(zone_, result, *left, *right)) - return false; - - OS::PathType type = OS::InspectPath(*result); - if (type == OS::Path_Folder) { - // If there is a folder matching this path, create a new subpackage, - // and then either return it (if this is the last link), or keep - // trying to import the rest of the path. - Local strpath(zone_, zone_->makeSymbol(*result)); - Local subpackage(zone_, Package::NewNormalized(zone_, package, strpath, path->name, Package::Normal)); - if (!subpackage) - return false; - if (!path->next) { - *out = subpackage; - return true; - } - return import(subpackage, path->next, out); - } - - // Otherwise, look for a file ending in ".sp". - AutoFree filepath((char *)zone_->malloc(strlen(*result) + strlen(".sp") + 1)); - if (!*filepath) - return false; - - strcpy(*filepath, *result); - strcat(*filepath, ".sp"); - - type = OS::InspectPath(*filepath); - if (type != OS::Path_File) - return true; - - // Create a new, empty module. - Local strpath(zone_, zone_->makeSymbol(*filepath)); - if (!strpath) - return false; - - Local module(zone_, Module::New(zone_, package, strpath, path->name)); - if (!module || !package->add(zone_, module)) - return false; - - *out = module; - return true; -} - -bool -PackageCache::import(Handle package, PathComponent *path, Importable **out) -{ - // If the package is jailed, none of its imports are visible. - if (package->jailed()) - return true; - - Local obj(zone_, package->import(path->name)); - if (!obj) { - // We couldn't find anything. Try the file system. - if (!checkfs(package, path, out)) - return false; - - // checkfs will keep trying to import, so if it fails, we just - // propagate up. - return true; - } - - // If we found an existing module, or the path has no next link, then - // we can't go any further. - if (obj->is(MapKind_Module) || !path->next) { - *out = obj; - return true; - } - - Local subpackage(zone_, Package::cast(obj)); - return import(subpackage, path->next, out); -} - -bool -PackageCache::findOrAdd(Handle package, PathComponent *path, Importable **out) -{ - // Local rooters should have initialized this, but whatever. - assert(*out == NULL); - - // Try to import locally first. - if (!import(package, path, out)) - return false; - if (*out) - return true; - - // Otherwise, search system roots. - Local root(zone_); - for (size_t i = 0; i < roots_.length(); i++) { - root = roots_[i]; - if (!import(root, path, out)) - return false; - if (*out) - return true; - } - - return true; -} - -PackageCache::~PackageCache() -{ -} +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#include +#include "Zone.h" +#include "Heap.h" +#include "Strings.h" +#include "Modules.h" +#include "Packages.h" +#include "FileSystem.h" +#include "List.h" +#include "Heap-inl.h" +#include "Handles-inl.h" + +using namespace ke; + +Package * +Package::NewNormalized(Zone *zone, Handle parent, Handle path, Handle name, unsigned flags) +{ + Local package(zone, Package::cast(zone->allocate(MapKind_Package, sizeof(Package), Heap::Tenure_Old))); + if (!package) + return NULL; + + // :TODO: assert path, name are atoms. + + package->flags_ = flags; + package->name_ = name; + package->parent_ = parent; + package->path_ = path; + package->nativeIdentity_ = parent ? parent->getNativeIdentity() : NULL; + + return package; +} + +Package * +Package::New(Zone *zone, Handle parent, const char *path, Handle name, unsigned flags) +{ + Local strpath(zone, zone->makeSymbol(path)); + if (!strpath) + return NULL; + + return Package::NewNormalized(zone, parent, strpath, name, flags); +} + +bool +Package::add(Zone *zone, Handle obj) +{ + Local pkg(zone, this); + + assert(obj->parent() == pkg); + + if (!pkg->contents_) { + pkg->contents_ = List::New(zone); + if (!pkg->contents_) + return false; + } + +#ifndef NDEBUG + // Assert no duplicate names. + for (unsigned i = 0; i < pkg->contents_->length(); i++) + assert(pkg->contents_->at(i)->name() != obj->name()); +#endif + + if (!pkg->contents_->append(zone, obj)) + return false; + + return true; +} + +Importable * +Package::import(Handle name) +{ + if (!contents_) + return NULL; + + for (unsigned i = 0; i < contents_->length(); i++) { + Importable *obj = contents_->at(i); + if (obj->isPackage() && Package::cast(obj)->name() == name) + return obj; + if (obj->isModule() && Module::cast(obj)->name() == name) + return obj; + } + + return NULL; +} + +PackageCache::PackageCache(Zone *zone) + : zone_(zone) +{ +} + +bool +PackageCache::initialize() +{ + return true; +} + +void +PackageCache::accept(VirtualObjectVisitor *visitor) +{ + for (size_t i = 0; i < roots_.length(); i++) + visitor->visit(roots_[i].address()); +} + +bool +PackageCache::checkfs(Handle package, PathComponent *path, Importable **out) +{ + AutoFree left(strdup(package->path()->chars())); + AutoFree right(strdup(path->name->chars())); + AutoFree result; + + if (!JoinPaths(zone_, result, *left, *right)) + return false; + + OS::PathType type = OS::InspectPath(*result); + if (type == OS::Path_Folder) { + // If there is a folder matching this path, create a new subpackage, + // and then either return it (if this is the last link), or keep + // trying to import the rest of the path. + Local strpath(zone_, zone_->makeSymbol(*result)); + Local subpackage(zone_, Package::NewNormalized(zone_, package, strpath, path->name, Package::Normal)); + if (!subpackage) + return false; + if (!path->next) { + *out = subpackage; + return true; + } + return import(subpackage, path->next, out); + } + + // Otherwise, look for a file ending in ".sp". + AutoFree filepath((char *)zone_->malloc(strlen(*result) + strlen(".sp") + 1)); + if (!*filepath) + return false; + + strcpy(*filepath, *result); + strcat(*filepath, ".sp"); + + type = OS::InspectPath(*filepath); + if (type != OS::Path_File) + return true; + + // Create a new, empty module. + Local strpath(zone_, zone_->makeSymbol(*filepath)); + if (!strpath) + return false; + + Local module(zone_, Module::New(zone_, package, strpath, path->name)); + if (!module || !package->add(zone_, module)) + return false; + + *out = module; + return true; +} + +bool +PackageCache::import(Handle package, PathComponent *path, Importable **out) +{ + // If the package is jailed, none of its imports are visible. + if (package->jailed()) + return true; + + Local obj(zone_, package->import(path->name)); + if (!obj) { + // We couldn't find anything. Try the file system. + if (!checkfs(package, path, out)) + return false; + + // checkfs will keep trying to import, so if it fails, we just + // propagate up. + return true; + } + + // If we found an existing module, or the path has no next link, then + // we can't go any further. + if (obj->is(MapKind_Module) || !path->next) { + *out = obj; + return true; + } + + Local subpackage(zone_, Package::cast(obj)); + return import(subpackage, path->next, out); +} + +bool +PackageCache::findOrAdd(Handle package, PathComponent *path, Importable **out) +{ + // Local rooters should have initialized this, but whatever. + assert(*out == NULL); + + // Try to import locally first. + if (!import(package, path, out)) + return false; + if (*out) + return true; + + // Otherwise, search system roots. + Local root(zone_); + for (size_t i = 0; i < roots_.length(); i++) { + root = roots_[i]; + if (!import(root, path, out)) + return false; + if (*out) + return true; + } + + return true; +} + +PackageCache::~PackageCache() +{ +} diff -r c4b1341297e5 -r ba85a47ee414 src/Packages.h --- a/src/Packages.h Mon Jun 10 00:46:29 2013 +0100 +++ b/src/Packages.h Mon Jun 10 01:04:43 2013 +0100 @@ -1,193 +1,193 @@ -/* - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#ifndef _include_sp2_packages_h_ -#define _include_sp2_packages_h_ - -#include "PoolAllocator.h" -#include "Objects.h" -#include "HashTable.h" - -namespace ke { - -class String; -class Module; -class Package; -class ImportStatement; -class VirtualObjectVisitor; - -template class List; - -struct PathComponent : PoolObject -{ - PathComponent(ScopedRoot *name, PathComponent *next) - : name(*name), - next(next) - { - } - ScopedRoot &name; - PathComponent *next; -}; - -class Importable : public Object -{ - public: - Package *parent() { - return parent_; - } - String *path() { - return path_; - } - String *name() { - return name_; - } - - bool isModule() { - return is(MapKind_Module); - } - bool isPackage() { - return is(MapKind_Package); - } - - static inline size_t offsetOfName() { - return OFFSETOF(Importable, name_); - } - static inline size_t offsetOfParent() { - return OFFSETOF(Importable, parent_); - } - static inline size_t offsetOfPath() { - return OFFSETOF(Importable, path_); - } - - static inline Importable *cast(Object *obj) { - assert(!obj || obj->is(MapKind_Module) || obj->is(MapKind_Package)); - return reinterpret_cast(obj); - } - - protected: - Barriered name_; - Barriered parent_; - Barriered path_; -}; - -// A package is a single folder in the file system, and contains any number of -// modules and sub-packages. The package hierarchy forms a tree. For simplicity -// the root of the tree is an anonymous package that contains no modules. -// -// Each package in the package hierarchy has two components of path information: -// (1) Canonical path. This is a normalized path used to compute the paths of -// modules. -// (2) Name. This is the package's name as it would appear in a path -// component. It is optional. -// -// Packages may be "hidden", meaning that even if they have names, their name -// is not accessible from any import statements. Packages may also be "jailed", -// meaning that other names in the package are not visible to each other. -// -// Packages are created in a few ways: -// -// (1) System Roots. A system root is a default search location for imports, -// and they are installed at the top-level of the hierarchy by the host -// application. A system root has a unique top-level name and path. -// System roots are always hidden. -// -// (2) Imports. When importing, the package hierarchy may be extended as -// new paths are discovered. Packages discovered this way are never -// hidden or jailed. -// -// (3) Via Package::New(), which creates an empty package. This can be used -// to create custom packages. -class Package : public Importable -{ - public: - enum { - Normal = 0, - - // These must match the flags in the public API for Package. - Hidden = (1 << 0), - Jailed = (1 << 1) - }; - - public: - static Package *NewNormalized(Zone *zone, - Handle parent, - Handle path, - Handle name, - unsigned flags); - static Package *New(Zone *zone, - Handle parent, - const char *path, - Handle name, - unsigned flags); - - bool add(Zone *zone, Handle component); - - bool hidden() { - return !!(flags_ & Package::Hidden); - } - bool jailed() { - return !!(flags_ & Package::Jailed); - } - - void setNativeIdentity(void *identity) { - nativeIdentity_ = identity; - } - void *getNativeIdentity() { - return nativeIdentity_; - } - Importable *import(Handle name); - - // Begin GC descriptor. - static inline size_t offsetOfContents() { - return OFFSETOF(Package, contents_); - } - // End GC descriptor. - - static inline Package *cast(Object *obj) { - assert(!obj || obj->is(MapKind_Package)); - return reinterpret_cast(obj); - } - - private: - unsigned flags_; - Barriered > contents_; - void *nativeIdentity_; -}; - -class PackageCache -{ - public: - PackageCache(Zone *zone); - ~PackageCache(); - bool initialize(); - - bool findOrAdd(Handle package, PathComponent *path, Importable **out); - - void accept(VirtualObjectVisitor *visitor); - - private: - bool import(Handle package, PathComponent *path, Importable **out); - bool checkfs(Handle package, PathComponent *path, Importable **out); - - private: - Zone *zone_; - Vector > roots_; -}; - -} // namespace ke - -#endif // _include_sp2_packages_h_ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_sp2_packages_h_ +#define _include_sp2_packages_h_ + +#include "PoolAllocator.h" +#include "Objects.h" +#include "HashTable.h" + +namespace ke { + +class String; +class Module; +class Package; +class ImportStatement; +class VirtualObjectVisitor; + +template class List; + +struct PathComponent : PoolObject +{ + PathComponent(ScopedRoot *name, PathComponent *next) + : name(*name), + next(next) + { + } + ScopedRoot &name; + PathComponent *next; +}; + +class Importable : public Object +{ + public: + Package *parent() { + return parent_; + } + String *path() { + return path_; + } + String *name() { + return name_; + } + + bool isModule() { + return is(MapKind_Module); + } + bool isPackage() { + return is(MapKind_Package); + } + + static inline size_t offsetOfName() { + return OFFSETOF(Importable, name_); + } + static inline size_t offsetOfParent() { + return OFFSETOF(Importable, parent_); + } + static inline size_t offsetOfPath() { + return OFFSETOF(Importable, path_); + } + + static inline Importable *cast(Object *obj) { + assert(!obj || obj->is(MapKind_Module) || obj->is(MapKind_Package)); + return reinterpret_cast(obj); + } + + protected: + Barriered name_; + Barriered parent_; + Barriered path_; +}; + +// A package is a single folder in the file system, and contains any number of +// modules and sub-packages. The package hierarchy forms a tree. For simplicity +// the root of the tree is an anonymous package that contains no modules. +// +// Each package in the package hierarchy has two components of path information: +// (1) Canonical path. This is a normalized path used to compute the paths of +// modules. +// (2) Name. This is the package's name as it would appear in a path +// component. It is optional. +// +// Packages may be "hidden", meaning that even if they have names, their name +// is not accessible from any import statements. Packages may also be "jailed", +// meaning that other names in the package are not visible to each other. +// +// Packages are created in a few ways: +// +// (1) System Roots. A system root is a default search location for imports, +// and they are installed at the top-level of the hierarchy by the host +// application. A system root has a unique top-level name and path. +// System roots are always hidden. +// +// (2) Imports. When importing, the package hierarchy may be extended as +// new paths are discovered. Packages discovered this way are never +// hidden or jailed. +// +// (3) Via Package::New(), which creates an empty package. This can be used +// to create custom packages. +class Package : public Importable +{ + public: + enum { + Normal = 0, + + // These must match the flags in the public API for Package. + Hidden = (1 << 0), + Jailed = (1 << 1) + }; + + public: + static Package *NewNormalized(Zone *zone, + Handle parent, + Handle path, + Handle name, + unsigned flags); + static Package *New(Zone *zone, + Handle parent, + const char *path, + Handle name, + unsigned flags); + + bool add(Zone *zone, Handle component); + + bool hidden() { + return !!(flags_ & Package::Hidden); + } + bool jailed() { + return !!(flags_ & Package::Jailed); + } + + void setNativeIdentity(void *identity) { + nativeIdentity_ = identity; + } + void *getNativeIdentity() { + return nativeIdentity_; + } + Importable *import(Handle name); + + // Begin GC descriptor. + static inline size_t offsetOfContents() { + return OFFSETOF(Package, contents_); + } + // End GC descriptor. + + static inline Package *cast(Object *obj) { + assert(!obj || obj->is(MapKind_Package)); + return reinterpret_cast(obj); + } + + private: + unsigned flags_; + Barriered > contents_; + void *nativeIdentity_; +}; + +class PackageCache +{ + public: + PackageCache(Zone *zone); + ~PackageCache(); + bool initialize(); + + bool findOrAdd(Handle package, PathComponent *path, Importable **out); + + void accept(VirtualObjectVisitor *visitor); + + private: + bool import(Handle package, PathComponent *path, Importable **out); + bool checkfs(Handle package, PathComponent *path, Importable **out); + + private: + Zone *zone_; + Vector > roots_; +}; + +} // namespace ke + +#endif // _include_sp2_packages_h_ diff -r c4b1341297e5 -r ba85a47ee414 src/PoolAllocator.cpp --- a/src/PoolAllocator.cpp Mon Jun 10 00:46:29 2013 +0100 +++ b/src/PoolAllocator.cpp Mon Jun 10 01:04:43 2013 +0100 @@ -1,131 +1,131 @@ -/* - * Copyright (C) 2012 David Anderson - * - * This file is part of JITCraft. - * - * JITCraft is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * JITCraft. If not, see http://www.gnu.org/licenses/. - */ -#include -#include -#include -#include "PoolAllocator.h" -#include "Zone.h" -#include "Spaces-inl.h" -#include "Heap-inl.h" - -using namespace ke; - -PoolAllocator::PoolAllocator() - : reserved_(NULL), - last_(NULL) -{ -} - -PoolAllocator::~PoolAllocator() -{ - unwind(NULL); - if (reserved_) - free(reserved_); -} - -char * -PoolAllocator::enter() -{ - if (!last_) - return NULL; - return last_->ptr; -} - -void -PoolAllocator::leave(char *pos) -{ - unwind(pos); -} - -void -PoolAllocator::unwind(char *pos) -{ - while (last_) { - if (pos && pos >= last_->base && pos < last_->end) - break; - Pool *prev = last_->prev; - { - if (last_->size() <= kMaxReserveSize && - (!reserved_ || reserved_->size() < last_->size())) - { - if (reserved_) - free(reserved_); - reserved_ = last_; - } else { - free(last_); - } - } - last_ = prev; - } - - if (!last_) { - assert(!pos); - return; - } - - last_->ptr = pos; -} - -void * -PoolAllocator::slowAllocate(size_t actualBytes) -{ - size_t bytesNeeded = actualBytes + sizeof(Pool); - if (bytesNeeded < kDefaultPoolSize) - bytesNeeded = kDefaultPoolSize; - - Pool *pool; - if (reserved_ && reserved_->size() >= bytesNeeded) { - pool = reserved_; - reserved_ = NULL; - } else { - pool = (Pool *)malloc(bytesNeeded); - if (!pool) { - ZONE()->reportOutOfMemory(); - return NULL; - } - pool->base = (char *)(pool + 1); - pool->end = (char *)pool + bytesNeeded; - } - pool->ptr = pool->base + actualBytes; - pool->prev = last_; - last_ = pool; - return pool->base; -} - -void -PoolAllocationPolicy::reportOutOfMemory() -{ - ZONE()->reportOutOfMemory(); -} - -void -PoolAllocationPolicy::reportAllocationOverflow() -{ - ZONE()->reportError(Message_AllocationOverflow); -} - -void * -PoolAllocationPolicy::malloc(size_t bytes) -{ - return ZONE()->pool().rawAllocate(bytes); -} - -void -PoolAllocationPolicy::free(void *ptr) -{ -} +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include +#include +#include +#include "PoolAllocator.h" +#include "Zone.h" +#include "Spaces-inl.h" +#include "Heap-inl.h" + +using namespace ke; + +PoolAllocator::PoolAllocator() + : reserved_(NULL), + last_(NULL) +{ +} + +PoolAllocator::~PoolAllocator() +{ + unwind(NULL); + if (reserved_) + free(reserved_); +} + +char * +PoolAllocator::enter() +{ + if (!last_) + return NULL; + return last_->ptr; +} + +void +PoolAllocator::leave(char *pos) +{ + unwind(pos); +} + +void +PoolAllocator::unwind(char *pos) +{ + while (last_) { + if (pos && pos >= last_->base && pos < last_->end) + break; + Pool *prev = last_->prev; + { + if (last_->size() <= kMaxReserveSize && + (!reserved_ || reserved_->size() < last_->size())) + { + if (reserved_) + free(reserved_); + reserved_ = last_; + } else { + free(last_); + } + } + last_ = prev; + } + + if (!last_) { + assert(!pos); + return; + } + + last_->ptr = pos; +} + +void * +PoolAllocator::slowAllocate(size_t actualBytes) +{ + size_t bytesNeeded = actualBytes + sizeof(Pool); + if (bytesNeeded < kDefaultPoolSize) + bytesNeeded = kDefaultPoolSize; + + Pool *pool; + if (reserved_ && reserved_->size() >= bytesNeeded) { + pool = reserved_; + reserved_ = NULL; + } else { + pool = (Pool *)malloc(bytesNeeded); + if (!pool) { + ZONE()->reportOutOfMemory(); + return NULL; + } + pool->base = (char *)(pool + 1); + pool->end = (char *)pool + bytesNeeded; + } + pool->ptr = pool->base + actualBytes; + pool->prev = last_; + last_ = pool; + return pool->base; +} + +void +PoolAllocationPolicy::reportOutOfMemory() +{ + ZONE()->reportOutOfMemory(); +} + +void +PoolAllocationPolicy::reportAllocationOverflow() +{ + ZONE()->reportError(Message_AllocationOverflow); +} + +void * +PoolAllocationPolicy::malloc(size_t bytes) +{ + return ZONE()->pool().rawAllocate(bytes); +} + +void +PoolAllocationPolicy::free(void *ptr) +{ +} diff -r c4b1341297e5 -r ba85a47ee414 src/PoolAllocator.h --- a/src/PoolAllocator.h Mon Jun 10 00:46:29 2013 +0100 +++ b/src/PoolAllocator.h Mon Jun 10 01:04:43 2013 +0100 @@ -1,145 +1,145 @@ -/* - * Copyright (C) 2012 David Anderson - * - * This file is part of JITCraft. - * - * JITCraft is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * JITCraft. If not, see http://www.gnu.org/licenses/. - */ -#ifndef _include_jitcraft_pool_allocator_h_ -#define _include_jitcraft_pool_allocator_h_ - -#include -#include -#include -#include "Utility.h" -#include "Vector.h" - -namespace ke { - -// Allocates memory in chunks that are not freed until the entire allocator -// is freed. This is intended for use with large, temporary data structures. -class PoolAllocator -{ - friend class PoolAllocationScope; - - struct Pool { - char *base; - char *ptr; - char *end; - Pool *prev; - - size_t size() const { - return size_t(end - base); - } - }; - - static const size_t kDefaultPoolSize = 8 * 1024; - static const size_t kMaxReserveSize = 64 * 1024; - - private: - Pool *reserved_; - Pool *last_; - - private: - void unwind(char *pos); - void *slowAllocate(size_t actualBytes); - - public: - PoolAllocator(); - ~PoolAllocator(); - - void *rawAllocate(size_t bytes) { - // Guarantee malloc alignment. - size_t actualBytes = Align(bytes, kMallocAlignment); - if (!last_ || (size_t(last_->end - last_->ptr) < actualBytes)) - return slowAllocate(actualBytes); - char *ptr = last_->ptr; - last_->ptr += actualBytes; - return ptr; - } - - template - T *alloc(size_t count = 1) { - void *ptr = rawAllocate(count * sizeof(T)); - if (!ptr) - return NULL; - - return reinterpret_cast(ptr); - } - - char *enter(); - void leave(char *position); -}; - -class PoolScope -{ - PoolAllocator &pool_; - char *position_; - - public: - PoolScope(PoolAllocator &allocator) - : pool_(allocator), - position_(allocator.enter()) - { - } - ~PoolScope() - { - pool_.leave(position_); - } -}; - -class PoolObject -{ - public: - void *operator new(size_t size, PoolAllocator &pool) { - return pool.rawAllocate(size); - } - void *operator new [](size_t size, PoolAllocator &pool) { - return pool.rawAllocate(size); - } - - // Using delete on pool-allocated objects is illegal. - void operator delete(void *ptr, PoolAllocator &pool) { - assert(false); - } - void operator delete [](void *ptr, PoolAllocator &pool) { - assert(false); - } -}; - -class PoolAllocationPolicy -{ - protected: - void reportAllocationOverflow(); - void reportOutOfMemory(); - - public: - void *malloc(size_t bytes); - void free(void *ptr); -}; - -template -class PoolList - : public Vector, - public PoolObject -{ - public: - PoolList() - : Vector(PoolAllocationPolicy()) - { - } -}; - -} - -#endif // _include_jitcraft_pool_allocator_h_ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_pool_allocator_h_ +#define _include_jitcraft_pool_allocator_h_ + +#include +#include +#include +#include "Utility.h" +#include "Vector.h" + +namespace ke { + +// Allocates memory in chunks that are not freed until the entire allocator +// is freed. This is intended for use with large, temporary data structures. +class PoolAllocator +{ + friend class PoolAllocationScope; + + struct Pool { + char *base; + char *ptr; + char *end; + Pool *prev; + + size_t size() const { + return size_t(end - base); + } + }; + + static const size_t kDefaultPoolSize = 8 * 1024; + static const size_t kMaxReserveSize = 64 * 1024; + + private: + Pool *reserved_; + Pool *last_; + + private: + void unwind(char *pos); + void *slowAllocate(size_t actualBytes); + + public: + PoolAllocator(); + ~PoolAllocator(); + + void *rawAllocate(size_t bytes) { + // Guarantee malloc alignment. + size_t actualBytes = Align(bytes, kMallocAlignment); + if (!last_ || (size_t(last_->end - last_->ptr) < actualBytes)) + return slowAllocate(actualBytes); + char *ptr = last_->ptr; + last_->ptr += actualBytes; + return ptr; + } + + template + T *alloc(size_t count = 1) { + void *ptr = rawAllocate(count * sizeof(T)); + if (!ptr) + return NULL; + + return reinterpret_cast(ptr); + } + + char *enter(); + void leave(char *position); +}; + +class PoolScope +{ + PoolAllocator &pool_; + char *position_; + + public: + PoolScope(PoolAllocator &allocator) + : pool_(allocator), + position_(allocator.enter()) + { + } + ~PoolScope() + { + pool_.leave(position_); + } +}; + +class PoolObject +{ + public: + void *operator new(size_t size, PoolAllocator &pool) { + return pool.rawAllocate(size); + } + void *operator new [](size_t size, PoolAllocator &pool) { + return pool.rawAllocate(size); + } + + // Using delete on pool-allocated objects is illegal. + void operator delete(void *ptr, PoolAllocator &pool) { + assert(false); + } + void operator delete [](void *ptr, PoolAllocator &pool) { + assert(false); + } +}; + +class PoolAllocationPolicy +{ + protected: + void reportAllocationOverflow(); + void reportOutOfMemory(); + + public: + void *malloc(size_t bytes); + void free(void *ptr); +}; + +template +class PoolList + : public Vector, + public PoolObject +{ + public: + PoolList() + : Vector(PoolAllocationPolicy()) + { + } +}; + +} + +#endif // _include_jitcraft_pool_allocator_h_ diff -r c4b1341297e5 -r ba85a47ee414 src/Reference.cpp --- a/src/Reference.cpp Mon Jun 10 00:46:29 2013 +0100 +++ b/src/Reference.cpp Mon Jun 10 01:04:43 2013 +0100 @@ -1,51 +1,51 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#include "Reference.h" -#include "Zone.h" -#include "Array.h" -#include "Structures.h" -#include "Heap-inl.h" - -using namespace ke; - -Reference * -Reference::NewStack(Zone *zone, void *location, Heap::Tenure tenure) -{ - Local ref(zone, Reference::cast(zone->allocate(MapKind_Reference, sizeof(Reference), tenure))); - if (!ref) - return NULL; - - ref->location_ = location; - ref->base_ = NULL; - return ref; -} - -Reference * -Reference::NewStack(Zone *zone, const LocalSlot &slot, Heap::Tenure tenure) -{ - Local ref(zone, Reference::cast(zone->allocate(MapKind_Reference, sizeof(Reference), tenure))); - if (!ref) - return NULL; - - ref->location_ = &ref->slot_; - ref->slot_ = slot; - ref->base_ = NULL; - return ref; -} - +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#include "Reference.h" +#include "Zone.h" +#include "Array.h" +#include "Structures.h" +#include "Heap-inl.h" + +using namespace ke; + +Reference * +Reference::NewStack(Zone *zone, void *location, Heap::Tenure tenure) +{ + Local ref(zone, Reference::cast(zone->allocate(MapKind_Reference, sizeof(Reference), tenure))); + if (!ref) + return NULL; + + ref->location_ = location; + ref->base_ = NULL; + return ref; +} + +Reference * +Reference::NewStack(Zone *zone, const LocalSlot &slot, Heap::Tenure tenure) +{ + Local ref(zone, Reference::cast(zone->allocate(MapKind_Reference, sizeof(Reference), tenure))); + if (!ref) + return NULL; + + ref->location_ = &ref->slot_; + ref->slot_ = slot; + ref->base_ = NULL; + return ref; +} + diff -r c4b1341297e5 -r ba85a47ee414 src/Reference.h --- a/src/Reference.h Mon Jun 10 00:46:29 2013 +0100 +++ b/src/Reference.h Mon Jun 10 01:04:43 2013 +0100 @@ -1,76 +1,76 @@ -/* - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#ifndef _include_jitcraft_reference_h_ -#define _include_jitcraft_reference_h_ - -#include "Heap.h" -#include "Barriers.h" -#include "Stack.h" - -namespace ke { - -class Array; -class Struct; - -// A Reference is a typed pointer to another location in memory. -class Reference : public Object -{ - void *location_; - Barriered base_; - - // We do not compute references to objects, so this does not need to be - // traced. - LocalSlot slot_; - - public: - static Reference *NewStack(Zone *zone, void *location, Heap::Tenure tenure = Heap::Tenure_Default); - static Reference *NewStack(Zone *zone, const LocalSlot &slot, Heap::Tenure tenure = Heap::Tenure_Default); - - int getInt() { - return *reinterpret_cast(location_); - } - void setInt(int i) { - *reinterpret_cast(location_) = i; - } - float getFloat() { - return *reinterpret_cast(location_); - } - void setFloat(float f) { - *reinterpret_cast(location_) = f; - } - - // WARNING! This should only be for Native calls. - void *location() { - return location_; - } - - // Begin GC Descriptor. - static inline size_t offsetOfBase() { - return offsetof(Reference, base_); - } - // End GC Descriptor. - - static inline Reference *cast(Object *obj) { - assert(!obj || obj->is(MapKind_Reference)); - return reinterpret_cast(obj); - } -}; - -} - -#endif // _include_jitcraft_reference_h_ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_reference_h_ +#define _include_jitcraft_reference_h_ + +#include "Heap.h" +#include "Barriers.h" +#include "Stack.h" + +namespace ke { + +class Array; +class Struct; + +// A Reference is a typed pointer to another location in memory. +class Reference : public Object +{ + void *location_; + Barriered base_; + + // We do not compute references to objects, so this does not need to be + // traced. + LocalSlot slot_; + + public: + static Reference *NewStack(Zone *zone, void *location, Heap::Tenure tenure = Heap::Tenure_Default); + static Reference *NewStack(Zone *zone, const LocalSlot &slot, Heap::Tenure tenure = Heap::Tenure_Default); + + int getInt() { + return *reinterpret_cast(location_); + } + void setInt(int i) { + *reinterpret_cast(location_) = i; + } + float getFloat() { + return *reinterpret_cast(location_); + } + void setFloat(float f) { + *reinterpret_cast(location_) = f; + } + + // WARNING! This should only be for Native calls. + void *location() { + return location_; + } + + // Begin GC Descriptor. + static inline size_t offsetOfBase() { + return offsetof(Reference, base_); + } + // End GC Descriptor. + + static inline Reference *cast(Object *obj) { + assert(!obj || obj->is(MapKind_Reference)); + return reinterpret_cast(obj); + } +}; + +} + +#endif // _include_jitcraft_reference_h_ diff -r c4b1341297e5 -r ba85a47ee414 src/RootScopes.h --- a/src/RootScopes.h Mon Jun 10 00:46:29 2013 +0100 +++ b/src/RootScopes.h Mon Jun 10 01:04:43 2013 +0100 @@ -1,106 +1,106 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#ifndef _include_jc_root_scopes_h_ -#define _include_jc_root_scopes_h_ - -#include "../include/keima.h" -#include "Objects.h" -#include "VirtualObjectVisitor.h" - -namespace ke { - -class RootScope; - -// A RootScopeManager is essentially a specialized PoolAllocator that can -// only allocate Object ** entries. It is used to allow huge, temporary -// heap data structures like ASTs to hold safe references to objects. When -// the data structures is no longer needed, all of the roots are removed -// at once. -class RootScopeManager -{ - static const size_t kObjectsPerBlock = 4096; - - struct Block { - Object *objects[kObjectsPerBlock]; - Object **cur; - Object **end; - Block *prev; - - Block(Block *prev) - : cur(objects), - end(objects + kObjectsPerBlock), - prev(prev) - { - } - }; - - private: - bool grow(); - void unwindSlow(Object **position); - - public: - RootScopeManager(); - ~RootScopeManager(); - - Object **position() const { - return blocks_->cur; - } - - void unwind(Object **position) { - if (position >= blocks_->cur && position <= blocks_->end) { - blocks_->cur = position; - return; - } - unwindSlow(position); - } - - Object **make(Object *obj) { - assert(scope_); - if (blocks_->cur + 1 > blocks_->end) { - if (!grow()) - return NULL; - } - Object **ptr = blocks_->cur; - blocks_->cur++; - *ptr = obj; - return ptr; - } - - Ke::HandleScope **scope() { - return &scope_; - } - - void accept(VirtualObjectVisitor *visitor); - - private: - Block *blocks_; - Block first_; - Ke::HandleScope *scope_; -}; - -class RootScope : public Ke::HandleScope -{ - public: - // These are implemented in Zone.h. - inline RootScope(Zone *zone); -}; - -} - -#endif // _include_jc_root_scope_h_ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jc_root_scopes_h_ +#define _include_jc_root_scopes_h_ + +#include "../include/keima.h" +#include "Objects.h" +#include "VirtualObjectVisitor.h" + +namespace ke { + +class RootScope; + +// A RootScopeManager is essentially a specialized PoolAllocator that can +// only allocate Object ** entries. It is used to allow huge, temporary +// heap data structures like ASTs to hold safe references to objects. When +// the data structures is no longer needed, all of the roots are removed +// at once. +class RootScopeManager +{ + static const size_t kObjectsPerBlock = 4096; + + struct Block { + Object *objects[kObjectsPerBlock]; + Object **cur; + Object **end; + Block *prev; + + Block(Block *prev) + : cur(objects), + end(objects + kObjectsPerBlock), + prev(prev) + { + } + }; + + private: + bool grow(); + void unwindSlow(Object **position); + + public: + RootScopeManager(); + ~RootScopeManager(); + + Object **position() const { + return blocks_->cur; + } + + void unwind(Object **position) { + if (position >= blocks_->cur && position <= blocks_->end) { + blocks_->cur = position; + return; + } + unwindSlow(position); + } + + Object **make(Object *obj) { + assert(scope_); + if (blocks_->cur + 1 > blocks_->end) { + if (!grow()) + return NULL; + } + Object **ptr = blocks_->cur; + blocks_->cur++; + *ptr = obj; + return ptr; + } + + Ke::HandleScope **scope() { + return &scope_; + } + + void accept(VirtualObjectVisitor *visitor); + + private: + Block *blocks_; + Block first_; + Ke::HandleScope *scope_; +}; + +class RootScope : public Ke::HandleScope +{ + public: + // These are implemented in Zone.h. + inline RootScope(Zone *zone); +}; + +} + +#endif // _include_jc_root_scope_h_ diff -r c4b1341297e5 -r ba85a47ee414 src/Stack.h --- a/src/Stack.h Mon Jun 10 00:46:29 2013 +0100 +++ b/src/Stack.h Mon Jun 10 01:04:43 2013 +0100 @@ -1,246 +1,246 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of JITCraft. - * - * JITCraft is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * JITCraft. If not, see http://www.gnu.org/licenses/. - */ -#ifndef _include_jc_stack_h_ -#define _include_jc_stack_h_ - -#include -#include "Utility.h" -#include "Code.h" -#include "Functions.h" -#include "VirtualObjectVisitor.h" - -namespace ke { - -class Object; -class Array; -class Reference; -class Frame; -class Stack; -class Zone; -class Module; - -union LocalSlot -{ - int i32; - float f; - Address address; - Object *obj; - Array *array; - Reference *ref; - Function *fun; - Module *module; -}; - -struct Top -{ - LocalSlot *sp; - bytecode *pc; - Frame *fp; -}; - -class Frame -{ - friend class Stack; - - Frame *prev_; - bytecode *prevpc_; - union { - Function *fun_; - Native *native_; - Object *callee_; - }; - Code *code_; - - // Position of the LIFO stack at function entry. - Address lifoPosition_; - - // Loop nesting level, for LIFO stack balancing. LIFO stack balancing - // is not strictly required, but it helps to avoid unnecessary waste, - // since once the LIFO stack is depleted, we must use the heap instead. - unsigned loopDepth_; - - public: - Frame(Frame *prev, bytecode *prevpc, Function *fun, Code *code, Address lifoPosition) - : prev_(prev), - prevpc_(prevpc), - fun_(fun), - code_(code), - lifoPosition_(lifoPosition), - loopDepth_(0) - { - } - - Frame(Frame *prev, bytecode *prevpc, Native *native, Address lifoPosition) - : prev_(prev), - prevpc_(prevpc), - native_(native), - code_(NULL), - lifoPosition_(lifoPosition) - { - } - - Address *loopStack() { - return reinterpret_cast
(this + 1); - } - LocalSlot *locals() { - return reinterpret_cast(loopStack() + code_->maxLoopDepth()); - } - LocalSlot *stackBase() { - return locals() + code_->nlocals(); - } - Function *fun() const { - assert(fun_ && callee_->is(MapKind_Function)); - return fun_; - } - Function *maybeFun() const { - return (fun_ && callee_->is(MapKind_Function)) ? fun_ : NULL; - } - bool isNative() const { - return callee_ && callee_->is(MapKind_Native); - } - Native *native() const { - assert(isNative()); - return native_; - } - Code *code() const { - assert(code_); - return code_; - } - Frame *prev() const { - return prev_; - } - bytecode *prevpc() const { - return prevpc_; - } - Address lifoPosition() const { - return lifoPosition_; - } - LocalSlot &arg(unsigned slot) { - assert(slot < fun()->nargs()); - return *(reinterpret_cast(this) - slot - 1); - } - void enterLoop(Zone *zone); - void leaveLoop(Zone *zone); - void accept(VirtualObjectVisitor *visitor); -}; - -// RAII object for calling a function from C++. -class InvokeArgs -{ - friend class Stack; - - Stack *stack_; - LocalSlot *args_; - unsigned nargs_; - - public: - InvokeArgs() - : stack_(NULL), - args_(NULL), - nargs_(0) - { - } - - ~InvokeArgs(); - - LocalSlot &arg(unsigned index) { - assert(index < nargs_); - return args_[nargs_ - index - 1]; - } -}; - -// RAII object for calling a native from C++. -class NativeArgs -{ - friend class Stack; - - Stack *stack_; - LocalSlot *argsBase_; - - public: - NativeArgs() - : stack_(NULL), - argsBase_(NULL) - { - } - - ~NativeArgs(); - - intptr_t *args() { - return reinterpret_cast(argsBase_); - } -}; - -class Stack -{ - friend class InvokeArgs; - friend class NativeArgs; - - private: - static const size_t STACK_SIZE = 1024 * 1024; - - public: - Stack(Zone *zone); - ~Stack(); - - bool initialize(); - - public: - Frame *pushInlineFrame(Handle fun); - Frame *popInlineFrame(); - - bool pushInvokeFrame(Handle fun, InvokeArgs &args); - bool pushModuleFrame(Handle module, InvokeArgs &args); - bool pushNativeFrame(Handle native, NativeArgs &args, unsigned argc); - - Top *top() { - return &top_; - } - bool isInCallStack(Handle module); - void accept(VirtualObjectVisitor *visitor); - - private: - void popInvokeFrame(InvokeArgs &args); - void popNativeFrame(NativeArgs &args); - - private: - Zone *zone_; - char *buffer_; - char *end_; - Top top_; -}; - -class StackIter -{ - bytecode *pc_; - Frame *fp_; - - public: - StackIter(Zone *zone); - - bool done() const; - const char *function() const; - unsigned lineNumber() const; - const char *source() const; - void prev(); -}; - -} - -#endif // _include_jc_stack_h_ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jc_stack_h_ +#define _include_jc_stack_h_ + +#include +#include "Utility.h" +#include "Code.h" +#include "Functions.h" +#include "VirtualObjectVisitor.h" + +namespace ke { + +class Object; +class Array; +class Reference; +class Frame; +class Stack; +class Zone; +class Module; + +union LocalSlot +{ + int i32; + float f; + Address address; + Object *obj; + Array *array; + Reference *ref; + Function *fun; + Module *module; +}; + +struct Top +{ + LocalSlot *sp; + bytecode *pc; + Frame *fp; +}; + +class Frame +{ + friend class Stack; + + Frame *prev_; + bytecode *prevpc_; + union { + Function *fun_; + Native *native_; + Object *callee_; + }; + Code *code_; + + // Position of the LIFO stack at function entry. + Address lifoPosition_; + + // Loop nesting level, for LIFO stack balancing. LIFO stack balancing + // is not strictly required, but it helps to avoid unnecessary waste, + // since once the LIFO stack is depleted, we must use the heap instead. + unsigned loopDepth_; + + public: + Frame(Frame *prev, bytecode *prevpc, Function *fun, Code *code, Address lifoPosition) + : prev_(prev), + prevpc_(prevpc), + fun_(fun), + code_(code), + lifoPosition_(lifoPosition), + loopDepth_(0) + { + } + + Frame(Frame *prev, bytecode *prevpc, Native *native, Address lifoPosition) + : prev_(prev), + prevpc_(prevpc), + native_(native), + code_(NULL), + lifoPosition_(lifoPosition) + { + } + + Address *loopStack() { + return reinterpret_cast
(this + 1); + } + LocalSlot *locals() { + return reinterpret_cast(loopStack() + code_->maxLoopDepth()); + } + LocalSlot *stackBase() { + return locals() + code_->nlocals(); + } + Function *fun() const { + assert(fun_ && callee_->is(MapKind_Function)); + return fun_; + } + Function *maybeFun() const { + return (fun_ && callee_->is(MapKind_Function)) ? fun_ : NULL; + } + bool isNative() const { + return callee_ && callee_->is(MapKind_Native); + } + Native *native() const { + assert(isNative()); + return native_; + } + Code *code() const { + assert(code_); + return code_; + } + Frame *prev() const { + return prev_; + } + bytecode *prevpc() const { + return prevpc_; + } + Address lifoPosition() const { + return lifoPosition_; + } + LocalSlot &arg(unsigned slot) { + assert(slot < fun()->nargs()); + return *(reinterpret_cast(this) - slot - 1); + } + void enterLoop(Zone *zone); + void leaveLoop(Zone *zone); + void accept(VirtualObjectVisitor *visitor); +}; + +// RAII object for calling a function from C++. +class InvokeArgs +{ + friend class Stack; + + Stack *stack_; + LocalSlot *args_; + unsigned nargs_; + + public: + InvokeArgs() + : stack_(NULL), + args_(NULL), + nargs_(0) + { + } + + ~InvokeArgs(); + + LocalSlot &arg(unsigned index) { + assert(index < nargs_); + return args_[nargs_ - index - 1]; + } +}; + +// RAII object for calling a native from C++. +class NativeArgs +{ + friend class Stack; + + Stack *stack_; + LocalSlot *argsBase_; + + public: + NativeArgs() + : stack_(NULL), + argsBase_(NULL) + { + } + + ~NativeArgs(); + + intptr_t *args() { + return reinterpret_cast(argsBase_); + } +}; + +class Stack +{ + friend class InvokeArgs; + friend class NativeArgs; + + private: + static const size_t STACK_SIZE = 1024 * 1024; + + public: + Stack(Zone *zone); + ~Stack(); + + bool initialize(); + + public: + Frame *pushInlineFrame(Handle fun); + Frame *popInlineFrame(); + + bool pushInvokeFrame(Handle fun, InvokeArgs &args); + bool pushModuleFrame(Handle module, InvokeArgs &args); + bool pushNativeFrame(Handle native, NativeArgs &args, unsigned argc); + + Top *top() { + return &top_; + } + bool isInCallStack(Handle module); + void accept(VirtualObjectVisitor *visitor); + + private: + void popInvokeFrame(InvokeArgs &args); + void popNativeFrame(NativeArgs &args); + + private: + Zone *zone_; + char *buffer_; + char *end_; + Top top_; +}; + +class StackIter +{ + bytecode *pc_; + Frame *fp_; + + public: + StackIter(Zone *zone); + + bool done() const; + const char *function() const; + unsigned lineNumber() const; + const char *source() const; + void prev(); +}; + +} + +#endif // _include_jc_stack_h_ diff -r c4b1341297e5 -r ba85a47ee414 src/StringPool.cpp --- a/src/StringPool.cpp Mon Jun 10 00:46:29 2013 +0100 +++ b/src/StringPool.cpp Mon Jun 10 01:04:43 2013 +0100 @@ -1,72 +1,72 @@ -/* - * Copyright (C) 2012 David Anderson - * - * This file is part of JITCraft. - * - * JITCraft is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * JITCraft. If not, see http://www.gnu.org/licenses/. - */ -#include "Zone.h" -#include "StringPool.h" -#include "Strings.h" -#include "Spaces.h" -#include "Spaces-inl.h" -#include "Heap-inl.h" - -using namespace ke; - -StringPool::StringPool(Zone *zone) - : zone_(zone), - set_(ZoneAllocatorPolicy()) -{ -} - -StringPool::~StringPool() -{ -} - -bool -StringPool::initialize() -{ - if (!set_.init(32)) - return false; - return true; -} - -String * -StringPool::add(const char *str, size_t length) -{ - StringSet::Insert i = set_.findForAdd(CharsAndLength(str, length)); - if (i.found()) - return *i; - - Local obj(zone_, String::New(zone_, str, length, Heap::Tenure_Old)); - if (!obj) - return NULL; - - set_.add(i, obj); - return obj; -} - -void -StringPool::sweep() -{ - for (StringSet::iterator i(&set_); !i.empty(); i.next()) { - String *obj = *i; - MarkBit bits = HeapChunk::GetMarkBit(obj); - if (HeapChunk::IsWhite(bits)) - i.erase(); - else - assert(HeapChunk::IsBlack(bits)); - } - set_.checkDensity(); -} +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include "Zone.h" +#include "StringPool.h" +#include "Strings.h" +#include "Spaces.h" +#include "Spaces-inl.h" +#include "Heap-inl.h" + +using namespace ke; + +StringPool::StringPool(Zone *zone) + : zone_(zone), + set_(ZoneAllocatorPolicy()) +{ +} + +StringPool::~StringPool() +{ +} + +bool +StringPool::initialize() +{ + if (!set_.init(32)) + return false; + return true; +} + +String * +StringPool::add(const char *str, size_t length) +{ + StringSet::Insert i = set_.findForAdd(CharsAndLength(str, length)); + if (i.found()) + return *i; + + Local obj(zone_, String::New(zone_, str, length, Heap::Tenure_Old)); + if (!obj) + return NULL; + + set_.add(i, obj); + return obj; +} + +void +StringPool::sweep() +{ + for (StringSet::iterator i(&set_); !i.empty(); i.next()) { + String *obj = *i; + MarkBit bits = HeapChunk::GetMarkBit(obj); + if (HeapChunk::IsWhite(bits)) + i.erase(); + else + assert(HeapChunk::IsBlack(bits)); + } + set_.checkDensity(); +} diff -r c4b1341297e5 -r ba85a47ee414 src/StringPool.h --- a/src/StringPool.h Mon Jun 10 00:46:29 2013 +0100 +++ b/src/StringPool.h Mon Jun 10 01:04:43 2013 +0100 @@ -1,68 +1,68 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#ifndef _include_jitcraft_string_pool_h_ -#define _include_jitcraft_string_pool_h_ - -#include -#include "AllocatorPolicies.h" -#include "HashTable.h" -#include "Strings.h" - -namespace ke { - -class Zone; -class String; - -// Guarantee that strings with the same contents will map to one pointer. -class StringPool -{ - struct Policy - { - typedef String * Payload; - - static uint32 hash(const CharsAndLength &key) { - return HashCharSequence(key.str(), key.length()); - } - - static bool matches(const CharsAndLength &key, String *string) { - if (string->length() != key.length()) - return false; - return memcmp(string->chars(), key.str(), key.length() * sizeof(char)) == 0; - } - }; - - typedef HashTable StringSet; - - public: - StringPool(Zone *zone); - ~StringPool(); - bool initialize(); - - public: - String *add(const char *str, size_t len); - void sweep(); - - private: - Zone *zone_; - StringSet set_; -}; - -} - -#endif // _include_jitcraft_string_pool_h_ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_string_pool_h_ +#define _include_jitcraft_string_pool_h_ + +#include +#include "AllocatorPolicies.h" +#include "HashTable.h" +#include "Strings.h" + +namespace ke { + +class Zone; +class String; + +// Guarantee that strings with the same contents will map to one pointer. +class StringPool +{ + struct Policy + { + typedef String * Payload; + + static uint32 hash(const CharsAndLength &key) { + return HashCharSequence(key.str(), key.length()); + } + + static bool matches(const CharsAndLength &key, String *string) { + if (string->length() != key.length()) + return false; + return memcmp(string->chars(), key.str(), key.length() * sizeof(char)) == 0; + } + }; + + typedef HashTable StringSet; + + public: + StringPool(Zone *zone); + ~StringPool(); + bool initialize(); + + public: + String *add(const char *str, size_t len); + void sweep(); + + private: + Zone *zone_; + StringSet set_; +}; + +} + +#endif // _include_jitcraft_string_pool_h_ diff -r c4b1341297e5 -r ba85a47ee414 src/Strings.cpp --- a/src/Strings.cpp Mon Jun 10 00:46:29 2013 +0100 +++ b/src/Strings.cpp Mon Jun 10 01:04:43 2013 +0100 @@ -1,44 +1,44 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of JITCraft. - * - * JITCraft is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * JITCraft. If not, see http://www.gnu.org/licenses/. - */ -#include -#include "Strings.h" -#include "Zone.h" -#include "Heap-inl.h" - -using namespace ke; - -String * -String::New(Zone *zone, const char *from, Heap::Tenure tenure) -{ - return New(zone, from, strlen(from), tenure); -} - -String * -String::New(Zone *zone, const char *from, size_t length, Heap::Tenure tenure) -{ - size_t bytes = SizeFor(length); - Local string(zone, String::cast(zone->allocate(MapKind_String, bytes, tenure))); - if (!string) - return NULL; - - string->length_ = length; - memcpy(string->chars(), from, length + 1); - - return string; -} +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include +#include "Strings.h" +#include "Zone.h" +#include "Heap-inl.h" + +using namespace ke; + +String * +String::New(Zone *zone, const char *from, Heap::Tenure tenure) +{ + return New(zone, from, strlen(from), tenure); +} + +String * +String::New(Zone *zone, const char *from, size_t length, Heap::Tenure tenure) +{ + size_t bytes = SizeFor(length); + Local string(zone, String::cast(zone->allocate(MapKind_String, bytes, tenure))); + if (!string) + return NULL; + + string->length_ = length; + memcpy(string->chars(), from, length + 1); + + return string; +} diff -r c4b1341297e5 -r ba85a47ee414 src/Strings.h --- a/src/Strings.h Mon Jun 10 00:46:29 2013 +0100 +++ b/src/Strings.h Mon Jun 10 01:04:43 2013 +0100 @@ -1,80 +1,80 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of JITCraft. - * - * JITCraft is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * JITCraft. If not, see http://www.gnu.org/licenses/. - */ -#ifndef _include_jitcraft_string_h_ -#define _include_jitcraft_string_h_ - -#include "Heap.h" - -namespace ke { - -// A fixed-length string containing UTF-8 characters. -class String : public Object -{ - uint32 length_; - - public: - static String *New(Zone *zone, const char *from, Heap::Tenure tenure = Heap::Tenure_Default); - static String *New(Zone *zone, const char *from, size_t length, Heap::Tenure tenure = Heap::Tenure_Default); - - unsigned length() { - return length_; - } - char *chars() { - return reinterpret_cast(this + 1); - } - char *chars(Zone *zone) { - return chars(); - } - size_t objSize() { - return SizeFor(length()); - } - - static size_t SizeFor(unsigned length) { - return sizeof(String) + sizeof(char) * (length + 1); - } - - static inline String *cast(Object *obj) { - assert(!obj || obj->is(MapKind_String)); - return reinterpret_cast(obj); - } -}; - -class CharsAndLength -{ - const char *str_; - size_t length_; - - public: - CharsAndLength(const char *str, size_t length) - : str_(str), - length_(length) - { - } - - const char *str() const { - return str_; - } - size_t length() const { - return length_; - } -}; - -} - -#endif // _include_jitcraft_string_h_ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_string_h_ +#define _include_jitcraft_string_h_ + +#include "Heap.h" + +namespace ke { + +// A fixed-length string containing UTF-8 characters. +class String : public Object +{ + uint32 length_; + + public: + static String *New(Zone *zone, const char *from, Heap::Tenure tenure = Heap::Tenure_Default); + static String *New(Zone *zone, const char *from, size_t length, Heap::Tenure tenure = Heap::Tenure_Default); + + unsigned length() { + return length_; + } + char *chars() { + return reinterpret_cast(this + 1); + } + char *chars(Zone *zone) { + return chars(); + } + size_t objSize() { + return SizeFor(length()); + } + + static size_t SizeFor(unsigned length) { + return sizeof(String) + sizeof(char) * (length + 1); + } + + static inline String *cast(Object *obj) { + assert(!obj || obj->is(MapKind_String)); + return reinterpret_cast(obj); + } +}; + +class CharsAndLength +{ + const char *str_; + size_t length_; + + public: + CharsAndLength(const char *str, size_t length) + : str_(str), + length_(length) + { + } + + const char *str() const { + return str_; + } + size_t length() const { + return length_; + } +}; + +} + +#endif // _include_jitcraft_string_h_ diff -r c4b1341297e5 -r ba85a47ee414 src/Structures.cpp --- a/src/Structures.cpp Mon Jun 10 00:46:29 2013 +0100 +++ b/src/Structures.cpp Mon Jun 10 01:04:43 2013 +0100 @@ -1,205 +1,205 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#include -#include "Structures.h" -#include "Zone.h" -#include "Types.h" -#include "Array.h" -#include "Spaces-inl.h" -#include "Heap-inl.h" - -using namespace ke; - -StructureBufferBuilder::StructureBufferBuilder() - : size_(0) -{ -} - -bool -StructureBufferBuilder::add(Handle type, unsigned *offset) -{ - *offset = size_; - - if (!type->isTraceable()) { - // For now, assume the alignment of all primitive storage is 4 bytes, - // and that all need <= 4 bytes of storage. - assert(type->isInt32() || - type->isFloat() || - type->isBool() || - type->isChar() || - type->isEnum()); - size_ += 4; - return true; - } - - if (!offsets_.append(size_)) - return false; - - size_ += sizeof(Object *); - return true; -} - -Descriptor * -Descriptor::New(Zone *zone, Handle name, Handle type, Visibility visibility) -{ - Local desc(zone, - Descriptor::cast(zone->allocate(MapKind_Descriptor, sizeof(Descriptor), Heap::Tenure_Old))); - if (!desc) - return NULL; - - desc->name_ = name; - desc->type_ = type; - desc->offset_ = unsigned(-1); - desc->visibility_ = visibility; - return desc; -} - -StructMap * -StructMap::New(Zone *zone, const StructureBufferBuilder &builder, Handle type) -{ - Vector composedIndexes; - for (size_t i = 0; i < type->fields()->length(); i++) { - Descriptor *desc = Descriptor::cast(type->fields()->at(i)); - if (desc->type()->isArray() || desc->type()->isStruct()) { - if (!composedIndexes.append(i)) - return NULL; - } - } - - Local composed(zone, FixedArray::New(zone, composedIndexes.length(), Heap::Tenure_Old)); - if (!composed) - return NULL; - - for (size_t i = 0; i < composedIndexes.length(); i++) { - Descriptor *desc = Descriptor::cast(type->fields()->at(composedIndexes[i])); - composed->set(zone, i, desc); - } - - size_t bytesNeeded = sizeof(StructMap) + - (builder.traceableOffsetCount() * sizeof(unsigned)); - - Local map(zone); - map = StructMap::cast(zone->allocateMap(MapKind_Struct, bytesNeeded, MapKind_MetaStruct)); - if (!map) - return NULL; - - map->structureSize_ = builder.structureSize(); - map->traceOffsetCount_ = builder.traceableOffsetCount(); - map->type_ = type; - map->composed_ = composed; - memcpy(const_cast(map->traceOffsets()), builder.offsets(), - sizeof(unsigned) * builder.traceableOffsetCount()); - - return map; -} - -Struct * -Struct::New(Zone *zone, Handle map, Heap::Tenure tenure) -{ - size_t bytesNeeded = SizeFor(map); - - Local obj(zone, Struct::cast(zone->allocate(map, bytesNeeded, tenure))); - if (!obj) - return NULL; - - memset((Struct *)obj + 1, 0, (bytesNeeded - sizeof(Struct))); - - // Initialize any sub-structures. Right now, this is performed as part of - // struct allocation, rather than inlined in bytecode, for simplicity. - if (map->composed()) { - Local desc(zone); - Local child(zone); - Local childStructMap(zone); - Local childArrayMap(zone); - - for (unsigned i = 0; i < map->composed()->length(); i++) { - desc = Descriptor::cast(map->composed()->at(i)); - - if (desc->type()->isStruct()) { - childStructMap = StructMap::cast(StructType::cast(desc->type())->newMap()); - child = Struct::New(zone, childStructMap, tenure); - } else if (desc->type()->isArray()) { - // :TODO: don't create empty arrays for ref slots here. - // Instead, make a separate Initialize function, so we - // can avoid double-creating arrays for module globals. - // (const char:x[] = "x") - ArrayType *type = ArrayType::cast(desc->type()); - childArrayMap = ArrayMap::cast(type->newMap()); - if (type->isFixedLength()) - child = Array::NewFixed(zone, childArrayMap, tenure); - else - child = Array::NewEmpty(zone, childArrayMap, tenure); - } - if (!child) - return NULL; - - obj->set(zone, desc, child); - } - } - - return obj; -} - -bool -Struct::Copy(Zone *zone, Handle left, Handle right) -{ - assert(left->map()->type() == right->map()->type()); - - Local type(zone, StructType::cast(left->map()->type())); - Local leftStruct(zone); - Local rightStruct(zone); - Local leftArray(zone); - Local rightArray(zone); - for (unsigned i = 0; i < type->fields()->length(); i++) { - Descriptor *desc = Descriptor::cast(type->fields()->at(i)); - - // If the field is a struct, we need to deep-copy. - if (desc->type()->isStruct()) { - leftStruct = Struct::cast(left->get(desc)); - rightStruct = Struct::cast(right->get(desc)); - if (!Struct::Copy(zone, leftStruct, rightStruct)) - return false; - } else if (desc->type()->isArray()) { - leftArray = Array::cast(left->get(desc)); - rightArray = Array::cast(right->get(desc)); - if (!Array::ShallowCopy(zone, leftArray, rightArray)) - return false; - } else if (desc->type()->isTraceable()) { - Object *obj = right->get(desc); - left->set(zone, desc, obj); - } else { - switch (desc->type()->pod()) { - case PrimitiveType_Int32: - case PrimitiveType_Char: - case PrimitiveType_Bool: - left->set(desc, right->get(desc)); - break; - - case PrimitiveType_Float: - left->set(desc, right->get(desc)); - break; - - default: - assert(false); - } - } - } - - return true; +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#include +#include "Structures.h" +#include "Zone.h" +#include "Types.h" +#include "Array.h" +#include "Spaces-inl.h" +#include "Heap-inl.h" + +using namespace ke; + +StructureBufferBuilder::StructureBufferBuilder() + : size_(0) +{ +} + +bool +StructureBufferBuilder::add(Handle type, unsigned *offset) +{ + *offset = size_; + + if (!type->isTraceable()) { + // For now, assume the alignment of all primitive storage is 4 bytes, + // and that all need <= 4 bytes of storage. + assert(type->isInt32() || + type->isFloat() || + type->isBool() || + type->isChar() || + type->isEnum()); + size_ += 4; + return true; + } + + if (!offsets_.append(size_)) + return false; + + size_ += sizeof(Object *); + return true; +} + +Descriptor * +Descriptor::New(Zone *zone, Handle name, Handle type, Visibility visibility) +{ + Local desc(zone, + Descriptor::cast(zone->allocate(MapKind_Descriptor, sizeof(Descriptor), Heap::Tenure_Old))); + if (!desc) + return NULL; + + desc->name_ = name; + desc->type_ = type; + desc->offset_ = unsigned(-1); + desc->visibility_ = visibility; + return desc; +} + +StructMap * +StructMap::New(Zone *zone, const StructureBufferBuilder &builder, Handle type) +{ + Vector composedIndexes; + for (size_t i = 0; i < type->fields()->length(); i++) { + Descriptor *desc = Descriptor::cast(type->fields()->at(i)); + if (desc->type()->isArray() || desc->type()->isStruct()) { + if (!composedIndexes.append(i)) + return NULL; + } + } + + Local composed(zone, FixedArray::New(zone, composedIndexes.length(), Heap::Tenure_Old)); + if (!composed) + return NULL; + + for (size_t i = 0; i < composedIndexes.length(); i++) { + Descriptor *desc = Descriptor::cast(type->fields()->at(composedIndexes[i])); + composed->set(zone, i, desc); + } + + size_t bytesNeeded = sizeof(StructMap) + + (builder.traceableOffsetCount() * sizeof(unsigned)); + + Local map(zone); + map = StructMap::cast(zone->allocateMap(MapKind_Struct, bytesNeeded, MapKind_MetaStruct)); + if (!map) + return NULL; + + map->structureSize_ = builder.structureSize(); + map->traceOffsetCount_ = builder.traceableOffsetCount(); + map->type_ = type; + map->composed_ = composed; + memcpy(const_cast(map->traceOffsets()), builder.offsets(), + sizeof(unsigned) * builder.traceableOffsetCount()); + + return map; +} + +Struct * +Struct::New(Zone *zone, Handle map, Heap::Tenure tenure) +{ + size_t bytesNeeded = SizeFor(map); + + Local obj(zone, Struct::cast(zone->allocate(map, bytesNeeded, tenure))); + if (!obj) + return NULL; + + memset((Struct *)obj + 1, 0, (bytesNeeded - sizeof(Struct))); + + // Initialize any sub-structures. Right now, this is performed as part of + // struct allocation, rather than inlined in bytecode, for simplicity. + if (map->composed()) { + Local desc(zone); + Local child(zone); + Local childStructMap(zone); + Local childArrayMap(zone); + + for (unsigned i = 0; i < map->composed()->length(); i++) { + desc = Descriptor::cast(map->composed()->at(i)); + + if (desc->type()->isStruct()) { + childStructMap = StructMap::cast(StructType::cast(desc->type())->newMap()); + child = Struct::New(zone, childStructMap, tenure); + } else if (desc->type()->isArray()) { + // :TODO: don't create empty arrays for ref slots here. + // Instead, make a separate Initialize function, so we + // can avoid double-creating arrays for module globals. + // (const char:x[] = "x") + ArrayType *type = ArrayType::cast(desc->type()); + childArrayMap = ArrayMap::cast(type->newMap()); + if (type->isFixedLength()) + child = Array::NewFixed(zone, childArrayMap, tenure); + else + child = Array::NewEmpty(zone, childArrayMap, tenure); + } + if (!child) + return NULL; + + obj->set(zone, desc, child); + } + } + + return obj; +} + +bool +Struct::Copy(Zone *zone, Handle left, Handle right) +{ + assert(left->map()->type() == right->map()->type()); + + Local type(zone, StructType::cast(left->map()->type())); + Local leftStruct(zone); + Local rightStruct(zone); + Local leftArray(zone); + Local rightArray(zone); + for (unsigned i = 0; i < type->fields()->length(); i++) { + Descriptor *desc = Descriptor::cast(type->fields()->at(i)); + + // If the field is a struct, we need to deep-copy. + if (desc->type()->isStruct()) { + leftStruct = Struct::cast(left->get(desc)); + rightStruct = Struct::cast(right->get(desc)); + if (!Struct::Copy(zone, leftStruct, rightStruct)) + return false; + } else if (desc->type()->isArray()) { + leftArray = Array::cast(left->get(desc)); + rightArray = Array::cast(right->get(desc)); + if (!Array::ShallowCopy(zone, leftArray, rightArray)) + return false; + } else if (desc->type()->isTraceable()) { + Object *obj = right->get(desc); + left->set(zone, desc, obj); + } else { + switch (desc->type()->pod()) { + case PrimitiveType_Int32: + case PrimitiveType_Char: + case PrimitiveType_Bool: + left->set(desc, right->get(desc)); + break; + + case PrimitiveType_Float: + left->set(desc, right->get(desc)); + break; + + default: + assert(false); + } + } + } + + return true; } \ No newline at end of file diff -r c4b1341297e5 -r ba85a47ee414 src/Structures.h --- a/src/Structures.h Mon Jun 10 00:46:29 2013 +0100 +++ b/src/Structures.h Mon Jun 10 01:04:43 2013 +0100 @@ -1,215 +1,215 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#ifndef _include_jc_structure_h_ -#define _include_jc_structure_h_ - -#include "Objects.h" -#include "Handles.h" -#include "Vector.h" -#include "Heap.h" - -namespace ke { - -class Type; -class String; -class FixedArray; -class StructType; - -// When describing composite structures, the GC must be able to trace interior -// references to Objects. To make this possible, maps that describe composite -// structures must provide a list of offsets into their objects that are -// traceable. StructureBufferBuilder assists with this. -class StructureBufferBuilder -{ - public: - StructureBufferBuilder(); - - bool add(Handle type, unsigned *offset); - - unsigned structureSize() const { - return size_; - } - unsigned traceableOffsetCount() const { - return offsets_.length(); - } - const unsigned *offsets() const { - return offsets_.buffer(); - } - - private: - Vector offsets_; - unsigned size_; -}; - -// Represents a property descriptor for a structure field. -class Descriptor : public Object -{ - public: - enum Visibility { - Private, - Protected, - Public - }; - - private: - // The name of the field. - Barriered name_; - - // The type of the field. - Barriered type_; - - // The offset into an object at which this field can be accessed. - unsigned offset_; - - Visibility visibility_; - - public: - static Descriptor *New(Zone *zone, Handle name, Handle type, Visibility visibility); - - String *name() { - return name_; - } - Type *type() { - return type_; - } - unsigned offset() { - assert(offset_ != unsigned(-1)); - return offset_; - } - void setOffset(unsigned offset) { - assert(offset_ == unsigned(-1)); - offset_ = offset; - } - - // Begin GC descriptor. - static inline size_t offsetOfName() { - return OFFSETOF(Descriptor, name_); - } - static inline size_t offsetOfType() { - return OFFSETOF(Descriptor, type_); - } - // End GC descriptor. - - static inline Descriptor *cast(Object *obj) { - assert(!obj || obj->is(MapKind_Descriptor)); - return reinterpret_cast(obj); - } -}; - -class StructMap : public Map -{ - unsigned structureSize_; - unsigned traceOffsetCount_; - Barriered type_; - - // List of descriptors which must be allocated upon initialization. - Barriered composed_; - - public: - static StructMap *New(Zone *zone, const StructureBufferBuilder &builder, Handle type); - - unsigned structureSize() { - return structureSize_; - } - unsigned traceOffsetCount() { - return traceOffsetCount_; - } - const unsigned *traceOffsets() { - return reinterpret_cast(this + 1); - } - size_t objSize() { - return sizeof(StructMap) + sizeof(unsigned) * traceOffsetCount(); - } - StructType *type() { - return type_; - } - FixedArray *composed() { - return composed_; - } - - void setType(StructType *type) { - assert(!type_); - type_ = type; - } - - // Begin GC descriptors. - static inline size_t offsetOfType() { - return OFFSETOF(StructMap, type_); - } - static inline size_t offsetOfComposed() { - return OFFSETOF(StructMap, composed_); - } - // End GC descriptors. - - static inline StructMap *cast(Object *obj) { - assert(!obj || obj->is(MapKind_MetaStruct)); - return reinterpret_cast(obj); - } -}; - -class Struct : public Object -{ - public: - static Struct *New(Zone *zone, Handle map, Heap::Tenure tenure = Heap::Tenure_Default); - - static bool Copy(Zone *zone, Handle dest, Handle src); - - StructMap *map() { - return StructMap::cast(Object::map()); - } - - void *ref(Descriptor *desc) { - return reinterpret_cast
(this) + offsetOfSlotOffset(desc->offset()); - } - - static inline size_t SizeFor(StructMap *map) { - return Max(sizeof(Struct) + map->structureSize(), kMinObjectSize); - } - - template T get(Descriptor *desc) { - return *reinterpret_cast(ref(desc)); - } - - // This must NOT be used with any Object * type! - // :TODO: prevent with type system - template void set(Descriptor *desc, const T &t) { - *reinterpret_cast(ref(desc)) = t; - } - - void set(Zone *zone, Descriptor *desc, Object *obj) { - // :TODO: write barrier - *reinterpret_cast( - reinterpret_cast
(this) + - offsetOfSlotOffset(desc->offset())) = obj; - } - - static inline size_t offsetOfSlotOffset(unsigned offset) { - return sizeof(Struct) + offset; - } - - static inline Struct *cast(Object *obj) { - assert(!obj || obj->is(MapKind_Struct)); - return reinterpret_cast(obj); - } -}; - -} - -#endif // _include_jc_structure_h_ - +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jc_structure_h_ +#define _include_jc_structure_h_ + +#include "Objects.h" +#include "Handles.h" +#include "Vector.h" +#include "Heap.h" + +namespace ke { + +class Type; +class String; +class FixedArray; +class StructType; + +// When describing composite structures, the GC must be able to trace interior +// references to Objects. To make this possible, maps that describe composite +// structures must provide a list of offsets into their objects that are +// traceable. StructureBufferBuilder assists with this. +class StructureBufferBuilder +{ + public: + StructureBufferBuilder(); + + bool add(Handle type, unsigned *offset); + + unsigned structureSize() const { + return size_; + } + unsigned traceableOffsetCount() const { + return offsets_.length(); + } + const unsigned *offsets() const { + return offsets_.buffer(); + } + + private: + Vector offsets_; + unsigned size_; +}; + +// Represents a property descriptor for a structure field. +class Descriptor : public Object +{ + public: + enum Visibility { + Private, + Protected, + Public + }; + + private: + // The name of the field. + Barriered name_; + + // The type of the field. + Barriered type_; + + // The offset into an object at which this field can be accessed. + unsigned offset_; + + Visibility visibility_; + + public: + static Descriptor *New(Zone *zone, Handle name, Handle type, Visibility visibility); + + String *name() { + return name_; + } + Type *type() { + return type_; + } + unsigned offset() { + assert(offset_ != unsigned(-1)); + return offset_; + } + void setOffset(unsigned offset) { + assert(offset_ == unsigned(-1)); + offset_ = offset; + } + + // Begin GC descriptor. + static inline size_t offsetOfName() { + return OFFSETOF(Descriptor, name_); + } + static inline size_t offsetOfType() { + return OFFSETOF(Descriptor, type_); + } + // End GC descriptor. + + static inline Descriptor *cast(Object *obj) { + assert(!obj || obj->is(MapKind_Descriptor)); + return reinterpret_cast(obj); + } +}; + +class StructMap : public Map +{ + unsigned structureSize_; + unsigned traceOffsetCount_; + Barriered type_; + + // List of descriptors which must be allocated upon initialization. + Barriered composed_; + + public: + static StructMap *New(Zone *zone, const StructureBufferBuilder &builder, Handle type); + + unsigned structureSize() { + return structureSize_; + } + unsigned traceOffsetCount() { + return traceOffsetCount_; + } + const unsigned *traceOffsets() { + return reinterpret_cast(this + 1); + } + size_t objSize() { + return sizeof(StructMap) + sizeof(unsigned) * traceOffsetCount(); + } + StructType *type() { + return type_; + } + FixedArray *composed() { + return composed_; + } + + void setType(StructType *type) { + assert(!type_); + type_ = type; + } + + // Begin GC descriptors. + static inline size_t offsetOfType() { + return OFFSETOF(StructMap, type_); + } + static inline size_t offsetOfComposed() { + return OFFSETOF(StructMap, composed_); + } + // End GC descriptors. + + static inline StructMap *cast(Object *obj) { + assert(!obj || obj->is(MapKind_MetaStruct)); + return reinterpret_cast(obj); + } +}; + +class Struct : public Object +{ + public: + static Struct *New(Zone *zone, Handle map, Heap::Tenure tenure = Heap::Tenure_Default); + + static bool Copy(Zone *zone, Handle dest, Handle src); + + StructMap *map() { + return StructMap::cast(Object::map()); + } + + void *ref(Descriptor *desc) { + return reinterpret_cast
(this) + offsetOfSlotOffset(desc->offset()); + } + + static inline size_t SizeFor(StructMap *map) { + return Max(sizeof(Struct) + map->structureSize(), kMinObjectSize); + } + + template T get(Descriptor *desc) { + return *reinterpret_cast(ref(desc)); + } + + // This must NOT be used with any Object * type! + // :TODO: prevent with type system + template void set(Descriptor *desc, const T &t) { + *reinterpret_cast(ref(desc)) = t; + } + + void set(Zone *zone, Descriptor *desc, Object *obj) { + // :TODO: write barrier + *reinterpret_cast( + reinterpret_cast
(this) + + offsetOfSlotOffset(desc->offset())) = obj; + } + + static inline size_t offsetOfSlotOffset(unsigned offset) { + return sizeof(Struct) + offset; + } + + static inline Struct *cast(Object *obj) { + assert(!obj || obj->is(MapKind_Struct)); + return reinterpret_cast(obj); + } +}; + +} + +#endif // _include_jc_structure_h_ + diff -r c4b1341297e5 -r ba85a47ee414 src/TypeManager.cpp --- a/src/TypeManager.cpp Mon Jun 10 00:46:29 2013 +0100 +++ b/src/TypeManager.cpp Mon Jun 10 01:04:43 2013 +0100 @@ -1,167 +1,167 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#include -#include "Types.h" -#include "TypeManager.h" -#include "Zone.h" -#include "Strings.h" -#include "Spaces-inl.h" -#include "Heap-inl.h" - -using namespace ke; - - -TypeManager::TypeManager(Zone *zone) - : zone_(zone), - qualTable_(ZoneAllocatorPolicy()) -{ -} - -bool -TypeManager::initialize() -{ - voidType_ = Type::NewVoid(zone_); - if (!voidType_) - return false; - - uncheckedType_ = Type::NewUnchecked(zone_); - if (!uncheckedType_) - return false; - - stringType_ = Type::NewString(zone_); - if (!stringType_) - return false; - - importableType_ = Type::NewImportable(zone_); - if (!importableType_) - return false; - - typeType_ = Type::NewType(zone_); - if (!typeType_) - return false; - - if ((primitiveTypes_[PrimitiveType_Int32] = Type::NewPrimitive(zone_, PrimitiveType_Int32)) == NULL) - return false; - if ((primitiveTypes_[PrimitiveType_Float] = Type::NewPrimitive(zone_, PrimitiveType_Float)) == NULL) - return false; - if ((primitiveTypes_[PrimitiveType_Native] = Type::NewPrimitive(zone_, PrimitiveType_Native)) == NULL) - return false; - if ((primitiveTypes_[PrimitiveType_Char] = Type::NewPrimitive(zone_, PrimitiveType_Char)) == NULL) - return false; - if ((primitiveTypes_[PrimitiveType_Bool] = Type::NewPrimitive(zone_, PrimitiveType_Bool)) == NULL) - return false; - - Local type(zone_); - for (unsigned i = 0; i < PrimitiveTypes_Total; i++) { - type = primitiveTypes_[i]; - if ((referenceTypes_[i] = ReferenceType::New(zone_, type)) == NULL) - return false; - } - - if (!qualTable_.init(32)) - return false; - - return true; -} - -ArrayType * -TypeManager::newArray(Handle contained, int elements) -{ - // :TODO: cache this. - return ArrayType::New(zone_, contained, elements); -} - -ArrayType * -TypeManager::newExternalArray(Handle contained) -{ - return ArrayType::NewExternal(zone_, contained); -} - -ReferenceType * -TypeManager::getReference(Handle type, bool isConst) -{ - if (!isConst && type->isPrimitive()) - return referenceTypes_[type->primitive()]; - - // :TODO: cache this. - return ReferenceType::New(zone_, type); -} - -EnumType * -TypeManager::newEnum(Handle name) -{ - return EnumType::New(zone_, name); -} - -Type * -TypeManager::qualify(Handle argType, TypeQualifiers quals) -{ - Local type(zone_, argType->unqualified()); - - if (!quals) - return type; - - LookupKey key(type, quals); - - // We can't use findForAdd because a GC could rehash the table. - TypeHashTable::Result r = qualTable_.find(key); - if (r.found()) - return *r; - - Type *quald = Type::NewQualified(zone_, quals, type); - if (!quald) - return NULL; - - // :TODO: assert-no-gc - TypeHashTable::Insert i = qualTable_.findForAdd(key); - assert(!i.found()); - - if (!qualTable_.add(i, quald)) - return NULL; - - return quald; -} - -void -TypeManager::accept(VirtualObjectVisitor *visitor) -{ - visitor->visit(voidType_.address()); - visitor->visit(uncheckedType_.address()); - visitor->visit(stringType_.address()); - visitor->visit(importableType_.address()); - visitor->visit(typeType_.address()); - for (unsigned i = 0; i < PrimitiveTypes_Total; i++) { - visitor->visit(primitiveTypes_[i].address()); - visitor->visit(referenceTypes_[i].address()); - } -} - -void -TypeManager::sweep() -{ - for (TypeHashTable::iterator i(&qualTable_); !i.empty(); i.next()) { - Type *obj = *i; - MarkBit bits = HeapChunk::GetMarkBit(obj); - if (HeapChunk::IsWhite(bits)) - i.erase(); - else - assert(HeapChunk::IsBlack(bits)); - } - qualTable_.checkDensity(); -} +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#include +#include "Types.h" +#include "TypeManager.h" +#include "Zone.h" +#include "Strings.h" +#include "Spaces-inl.h" +#include "Heap-inl.h" + +using namespace ke; + + +TypeManager::TypeManager(Zone *zone) + : zone_(zone), + qualTable_(ZoneAllocatorPolicy()) +{ +} + +bool +TypeManager::initialize() +{ + voidType_ = Type::NewVoid(zone_); + if (!voidType_) + return false; + + uncheckedType_ = Type::NewUnchecked(zone_); + if (!uncheckedType_) + return false; + + stringType_ = Type::NewString(zone_); + if (!stringType_) + return false; + + importableType_ = Type::NewImportable(zone_); + if (!importableType_) + return false; + + typeType_ = Type::NewType(zone_); + if (!typeType_) + return false; + + if ((primitiveTypes_[PrimitiveType_Int32] = Type::NewPrimitive(zone_, PrimitiveType_Int32)) == NULL) + return false; + if ((primitiveTypes_[PrimitiveType_Float] = Type::NewPrimitive(zone_, PrimitiveType_Float)) == NULL) + return false; + if ((primitiveTypes_[PrimitiveType_Native] = Type::NewPrimitive(zone_, PrimitiveType_Native)) == NULL) + return false; + if ((primitiveTypes_[PrimitiveType_Char] = Type::NewPrimitive(zone_, PrimitiveType_Char)) == NULL) + return false; + if ((primitiveTypes_[PrimitiveType_Bool] = Type::NewPrimitive(zone_, PrimitiveType_Bool)) == NULL) + return false; + + Local type(zone_); + for (unsigned i = 0; i < PrimitiveTypes_Total; i++) { + type = primitiveTypes_[i]; + if ((referenceTypes_[i] = ReferenceType::New(zone_, type)) == NULL) + return false; + } + + if (!qualTable_.init(32)) + return false; + + return true; +} + +ArrayType * +TypeManager::newArray(Handle contained, int elements) +{ + // :TODO: cache this. + return ArrayType::New(zone_, contained, elements); +} + +ArrayType * +TypeManager::newExternalArray(Handle contained) +{ + return ArrayType::NewExternal(zone_, contained); +} + +ReferenceType * +TypeManager::getReference(Handle type, bool isConst) +{ + if (!isConst && type->isPrimitive()) + return referenceTypes_[type->primitive()]; + + // :TODO: cache this. + return ReferenceType::New(zone_, type); +} + +EnumType * +TypeManager::newEnum(Handle name) +{ + return EnumType::New(zone_, name); +} + +Type * +TypeManager::qualify(Handle argType, TypeQualifiers quals) +{ + Local type(zone_, argType->unqualified()); + + if (!quals) + return type; + + LookupKey key(type, quals); + + // We can't use findForAdd because a GC could rehash the table. + TypeHashTable::Result r = qualTable_.find(key); + if (r.found()) + return *r; + + Type *quald = Type::NewQualified(zone_, quals, type); + if (!quald) + return NULL; + + // :TODO: assert-no-gc + TypeHashTable::Insert i = qualTable_.findForAdd(key); + assert(!i.found()); + + if (!qualTable_.add(i, quald)) + return NULL; + + return quald; +} + +void +TypeManager::accept(VirtualObjectVisitor *visitor) +{ + visitor->visit(voidType_.address()); + visitor->visit(uncheckedType_.address()); + visitor->visit(stringType_.address()); + visitor->visit(importableType_.address()); + visitor->visit(typeType_.address()); + for (unsigned i = 0; i < PrimitiveTypes_Total; i++) { + visitor->visit(primitiveTypes_[i].address()); + visitor->visit(referenceTypes_[i].address()); + } +} + +void +TypeManager::sweep() +{ + for (TypeHashTable::iterator i(&qualTable_); !i.empty(); i.next()) { + Type *obj = *i; + MarkBit bits = HeapChunk::GetMarkBit(obj); + if (HeapChunk::IsWhite(bits)) + i.erase(); + else + assert(HeapChunk::IsBlack(bits)); + } + qualTable_.checkDensity(); +} diff -r c4b1341297e5 -r ba85a47ee414 src/TypeManager.h --- a/src/TypeManager.h Mon Jun 10 00:46:29 2013 +0100 +++ b/src/TypeManager.h Mon Jun 10 01:04:43 2013 +0100 @@ -1,115 +1,115 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#ifndef _include_jitcraft_type_manager_h_ -#define _include_jitcraft_type_manager_h_ - -#include "Barriers.h" -#include "Types.h" -#include "HashTable.h" -#include "AllocatorPolicies.h" -#include "VirtualObjectVisitor.h" - -namespace ke { - -class String; - -class TypeManager -{ - struct LookupKey - { - Handle type; - TypeQualifiers quals; - - LookupKey(Handle type, TypeQualifiers quals) - : type(type), - quals(quals) - { - } - }; - - struct Policy - { - typedef Type * Payload; - - static uint32 hash(const LookupKey &key) { - // We'll need to rehash with moving GC. - return HashPointer(key.type); - } - - static bool matches(const LookupKey &key, Type *type) { - assert(type->hasTypeQualifiers()); - return key.type == type->unqualified() && - key.quals == type->quals(); - } - }; - - typedef HashTable TypeHashTable; - - public: - TypeManager(Zone *zone); - - bool initialize(); - - Type *getPrimitive(PrimitiveType type) { - return primitiveTypes_[type]; - } - - Type *getString() { - return stringType_; - } - Type *getVoid() { - return voidType_; - } - Type *getUnchecked() { - return uncheckedType_; - } - Type *getImportable() { - return importableType_; - } - Type *getType() { - return typeType_; - } - ReferenceType *getReference(Handle type, bool isConst); - ArrayType *newArray(Handle contained, int elements); - ArrayType *newExternalArray(Handle contained); - EnumType *newEnum(Handle name); - - void accept(VirtualObjectVisitor *visitor); - void sweep(); - - Type *qualify(Handle type, TypeQualifiers quals); - - private: - Zone *zone_; - - // Primitive type cache. - Unbarriered voidType_; - Unbarriered stringType_; - Unbarriered uncheckedType_; - Unbarriered importableType_; - Unbarriered typeType_; - Unbarriered primitiveTypes_[PrimitiveTypes_Total]; - Unbarriered referenceTypes_[PrimitiveTypes_Total]; - - TypeHashTable qualTable_; -}; - -} - -#endif // _include_jitcraft_type_manager_h_ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_type_manager_h_ +#define _include_jitcraft_type_manager_h_ + +#include "Barriers.h" +#include "Types.h" +#include "HashTable.h" +#include "AllocatorPolicies.h" +#include "VirtualObjectVisitor.h" + +namespace ke { + +class String; + +class TypeManager +{ + struct LookupKey + { + Handle type; + TypeQualifiers quals; + + LookupKey(Handle type, TypeQualifiers quals) + : type(type), + quals(quals) + { + } + }; + + struct Policy + { + typedef Type * Payload; + + static uint32 hash(const LookupKey &key) { + // We'll need to rehash with moving GC. + return HashPointer(key.type); + } + + static bool matches(const LookupKey &key, Type *type) { + assert(type->hasTypeQualifiers()); + return key.type == type->unqualified() && + key.quals == type->quals(); + } + }; + + typedef HashTable TypeHashTable; + + public: + TypeManager(Zone *zone); + + bool initialize(); + + Type *getPrimitive(PrimitiveType type) { + return primitiveTypes_[type]; + } + + Type *getString() { + return stringType_; + } + Type *getVoid() { + return voidType_; + } + Type *getUnchecked() { + return uncheckedType_; + } + Type *getImportable() { + return importableType_; + } + Type *getType() { + return typeType_; + } + ReferenceType *getReference(Handle type, bool isConst); + ArrayType *newArray(Handle contained, int elements); + ArrayType *newExternalArray(Handle contained); + EnumType *newEnum(Handle name); + + void accept(VirtualObjectVisitor *visitor); + void sweep(); + + Type *qualify(Handle type, TypeQualifiers quals); + + private: + Zone *zone_; + + // Primitive type cache. + Unbarriered voidType_; + Unbarriered stringType_; + Unbarriered uncheckedType_; + Unbarriered importableType_; + Unbarriered typeType_; + Unbarriered primitiveTypes_[PrimitiveTypes_Total]; + Unbarriered referenceTypes_[PrimitiveTypes_Total]; + + TypeHashTable qualTable_; +}; + +} + +#endif // _include_jitcraft_type_manager_h_ diff -r c4b1341297e5 -r ba85a47ee414 src/Types.cpp --- a/src/Types.cpp Mon Jun 10 00:46:29 2013 +0100 +++ b/src/Types.cpp Mon Jun 10 01:04:43 2013 +0100 @@ -1,333 +1,333 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#include -#include "Types.h" -#include "Zone.h" -#include "Strings.h" -#include "Array.h" -#include "Structures.h" -#include "Heap-inl.h" - -using namespace ke; - -void -Type::init(Kind kind, Type *root) -{ - this->kind_ = kind; - this->root_ = root ? root : this; -} - -Type * -Type::NewVoid(Zone *zone) -{ - Local type(zone, Type::cast(zone->allocate(MapKind_Type, sizeof(Type), Heap::Tenure_Old))); - if (!type) - return NULL; - - type->init(VOID); - return type; -} - -Type * -Type::NewImportable(Zone *zone) -{ - Local type(zone, Type::cast(zone->allocate(MapKind_Type, sizeof(Type), Heap::Tenure_Old))); - if (!type) - return NULL; - - type->init(IMPORTABLE); - return type; -} - -Type * -Type::NewString(Zone *zone) -{ - Local type(zone, Type::cast(zone->allocate(MapKind_Type, sizeof(Type), Heap::Tenure_Old))); - if (!type) - return NULL; - - type->init(STRING); - return type; -} - -Type * -Type::NewUnchecked(Zone *zone) -{ - Local type(zone, Type::cast(zone->allocate(MapKind_Type, sizeof(Type), Heap::Tenure_Old))); - if (!type) - return NULL; - - type->init(UNCHECKED); - return type; -} - -Type * -Type::NewType(Zone *zone) -{ - Local type(zone, Type::cast(zone->allocate(MapKind_Type, sizeof(Type), Heap::Tenure_Old))); - if (!type) - return NULL; - - type->init(TYPE); - return type; -} - -Type * -Type::NewPrimitive(Zone *zone, PrimitiveType prim) -{ - Local type(zone, Type::cast(zone->allocate(MapKind_Type, sizeof(Type), Heap::Tenure_Old))); - if (!type) - return NULL; - - type->init(PRIMITIVE); - type->primitive_ = prim; - return type; -} - -Type * -Type::NewQualified(Zone *zone, TypeQualifiers qual, Handle root) -{ - Local type(zone, Type::cast(zone->allocate(MapKind_Type, sizeof(Type), Heap::Tenure_Old))); - if (!type) - return NULL; - - type->init(QUALTYPE, root); - type->quals_ = qual; - return type; -} - -ArrayType * -ArrayType::New(Zone *zone, Handle contained, int elements) -{ - if (contained->isArray() && - ArrayType::cast(contained)->levels() == MAX_ARRAY_DEPTH) - { - zone->reportError(Message_MaximumArrayDepth); - return NULL; - } - - Local type(zone, (ArrayType *)zone->allocate(MapKind_ArrayType, sizeof(ArrayType), Heap::Tenure_Old)); - if (!type) - return NULL; - - type->init(ARRAY); - type->contained_ = contained; - type->elements_ = elements; - if (!contained->isArray()) - type->levels_ = 1; - else - type->levels_ = ArrayType::cast(contained)->levels() + 1; - - type->newMap_ = ArrayMap::New(zone, type); - if (!type->newMap_) - return NULL; - - return type; -} - -ArrayType * -ArrayType::NewExternal(Zone *zone, Handle contained) -{ - Local type(zone, (ArrayType *)zone->allocate(MapKind_ArrayType, sizeof(ArrayType), Heap::Tenure_Old)); - if (!type) - return NULL; - - assert(contained->isPrimitive()); - - type->init(EXTERNAL_ARRAY); - type->contained_ = contained; - type->elements_ = DYNAMIC_ARRAY_SIZE; - type->levels_ = 1; - return type; -} - -ReferenceType * -ReferenceType::New(Zone *zone, Handle contained) -{ - Local type(zone, (ReferenceType *)zone->allocate(MapKind_ReferenceType, sizeof(ReferenceType), Heap::Tenure_Old)); - if (!type) - return NULL; - - type->init(REFERENCE); - type->contained_ = contained; - return type; -} - -EnumType * -EnumType::New(Zone *zone, Handle name) -{ - Local type(zone, (EnumType *)zone->allocate(MapKind_EnumType, sizeof(EnumType), Heap::Tenure_Old)); - if (!type) - return NULL; - - type->init(ENUM); - type->name_ = name; - type->fields_ = NULL; - return type; -} - -StructType * -StructType::New(Zone *zone, Handle name, Handle fields) -{ - Local type(zone, (StructType *)zone->allocate(MapKind_StructType, sizeof(StructType), Heap::Tenure_Old)); - if (!type) - return NULL; - - type->init(STRUCT); - type->newMap_ = NULL; - type->fields_ = fields; - type->name_ = name; - - // Fill in field offsets. - StructureBufferBuilder builder; - Local fieldType(zone); - for (size_t i = 0; i < fields->length(); i++) { - Descriptor *desc = Descriptor::cast(fields->at(i)); - fieldType = desc->type(); - - unsigned offset; - if (!builder.add(fieldType, &offset)) - return NULL; - desc->setOffset(offset); - } - - Local map(zone, StructMap::New(zone, builder, type)); - if (!map) - return NULL; - - type->newMap_ = map; - - return type; -} - -Descriptor * -StructType::lookupField(String *name, unsigned *index) -{ - // :TODO: assert no GC - for (unsigned i = 0; i < fields_->length(); i++) { - Descriptor *desc = Descriptor::cast(fields_->at(i)); - if (desc->name() == name) { - if (index) - *index = i; - return desc; - } - } - - return NULL; -} - -FunctionType * -FunctionType::New(Zone *zone, Handle name, TokenKind token) -{ - Local type(zone, (FunctionType *)zone->allocate(MapKind_FunctionType, sizeof(FunctionType), Heap::Tenure_Old)); - if (!type) - return NULL; - - type->init(FUNCTION); - type->parameters_ = NULL; - type->defaults_ = NULL; - type->returnType_ = NULL; - type->name_ = name; - type->token_ = token; - return type; -} - -static size_t -FormatVA(char *buffer, size_t maxlen, const char *text, va_list ap) -{ - size_t len = vsnprintf(buffer, maxlen, text, ap); - if (len >= maxlen) { - buffer[maxlen - 1] = '\0'; - return maxlen - 1; - } - return len; -} - -static size_t -Format(char *buffer, size_t maxlen, const char *text, ...) -{ - va_list ap; - va_start(ap, text); - size_t len = FormatVA(buffer, maxlen, text, ap); - va_end(ap); - return len; -} - -static size_t -FormatTypeName(Type *type, char *buffer, size_t maxlen) -{ - if (type->isArray()) { - ArrayType *atype = ArrayType::cast(type); - Type *innermost = atype->innermost(); - - size_t len = FormatTypeName(innermost, buffer, maxlen); - for (;;) { - if (atype->isFixedLength()) - len += Format(buffer + len, maxlen - len, "[%d]", atype->fixedLength()); - else - len += Format(buffer + len, maxlen - len, "[]"); - if (!atype->contained()->isArray()) - break; - atype = ArrayType::cast(atype->contained()); - } - return len; - } - if (type->hasTypeQualifiers()) { - size_t len = 0; - if (type->quals() & TypeQual_Const) - len += Format(buffer, maxlen, "const "); - return len + FormatTypeName(type->unqualified(), buffer + len, maxlen - len); - } - if (type->isFunction()) - return Format(buffer, maxlen, "function"); - if (type->isVoid()) - return Format(buffer, maxlen, "void"); - if (type->isUnchecked()) - return Format(buffer, maxlen, "unchecked"); - if (type->isImportable()) - return Format(buffer, maxlen, "import"); - if (type->isEnum()) - return Format(buffer, maxlen, "%s", EnumType::cast(type)->name()->chars()); - if (type->isStruct()) - return Format(buffer, maxlen, "%s", StructType::cast(type)->name()->chars()); - if (type->isReference()) { - ReferenceType *ref = ReferenceType::cast(type); - size_t len = Format(buffer, maxlen, "&"); - return len + FormatTypeName(ref->contained(), buffer + len, maxlen - len); - } - switch (type->primitive()) { - case PrimitiveType_Bool: - return Format(buffer, maxlen, "bool"); - case PrimitiveType_Char: - return Format(buffer, maxlen, "char"); - case PrimitiveType_Int32: - return Format(buffer, maxlen, "int"); - case PrimitiveType_Float: - return Format(buffer, maxlen, "float"); - default: - return Format(buffer, maxlen, ""); - } -} - -const char * -ke::GetTypeName(Type *type, char *buffer, size_t maxlen) -{ - FormatTypeName(type, buffer, maxlen); - return buffer; -} +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#include +#include "Types.h" +#include "Zone.h" +#include "Strings.h" +#include "Array.h" +#include "Structures.h" +#include "Heap-inl.h" + +using namespace ke; + +void +Type::init(Kind kind, Type *root) +{ + this->kind_ = kind; + this->root_ = root ? root : this; +} + +Type * +Type::NewVoid(Zone *zone) +{ + Local type(zone, Type::cast(zone->allocate(MapKind_Type, sizeof(Type), Heap::Tenure_Old))); + if (!type) + return NULL; + + type->init(VOID); + return type; +} + +Type * +Type::NewImportable(Zone *zone) +{ + Local type(zone, Type::cast(zone->allocate(MapKind_Type, sizeof(Type), Heap::Tenure_Old))); + if (!type) + return NULL; + + type->init(IMPORTABLE); + return type; +} + +Type * +Type::NewString(Zone *zone) +{ + Local type(zone, Type::cast(zone->allocate(MapKind_Type, sizeof(Type), Heap::Tenure_Old))); + if (!type) + return NULL; + + type->init(STRING); + return type; +} + +Type * +Type::NewUnchecked(Zone *zone) +{ + Local type(zone, Type::cast(zone->allocate(MapKind_Type, sizeof(Type), Heap::Tenure_Old))); + if (!type) + return NULL; + + type->init(UNCHECKED); + return type; +} + +Type * +Type::NewType(Zone *zone) +{ + Local type(zone, Type::cast(zone->allocate(MapKind_Type, sizeof(Type), Heap::Tenure_Old))); + if (!type) + return NULL; + + type->init(TYPE); + return type; +} + +Type * +Type::NewPrimitive(Zone *zone, PrimitiveType prim) +{ + Local type(zone, Type::cast(zone->allocate(MapKind_Type, sizeof(Type), Heap::Tenure_Old))); + if (!type) + return NULL; + + type->init(PRIMITIVE); + type->primitive_ = prim; + return type; +} + +Type * +Type::NewQualified(Zone *zone, TypeQualifiers qual, Handle root) +{ + Local type(zone, Type::cast(zone->allocate(MapKind_Type, sizeof(Type), Heap::Tenure_Old))); + if (!type) + return NULL; + + type->init(QUALTYPE, root); + type->quals_ = qual; + return type; +} + +ArrayType * +ArrayType::New(Zone *zone, Handle contained, int elements) +{ + if (contained->isArray() && + ArrayType::cast(contained)->levels() == MAX_ARRAY_DEPTH) + { + zone->reportError(Message_MaximumArrayDepth); + return NULL; + } + + Local type(zone, (ArrayType *)zone->allocate(MapKind_ArrayType, sizeof(ArrayType), Heap::Tenure_Old)); + if (!type) + return NULL; + + type->init(ARRAY); + type->contained_ = contained; + type->elements_ = elements; + if (!contained->isArray()) + type->levels_ = 1; + else + type->levels_ = ArrayType::cast(contained)->levels() + 1; + + type->newMap_ = ArrayMap::New(zone, type); + if (!type->newMap_) + return NULL; + + return type; +} + +ArrayType * +ArrayType::NewExternal(Zone *zone, Handle contained) +{ + Local type(zone, (ArrayType *)zone->allocate(MapKind_ArrayType, sizeof(ArrayType), Heap::Tenure_Old)); + if (!type) + return NULL; + + assert(contained->isPrimitive()); + + type->init(EXTERNAL_ARRAY); + type->contained_ = contained; + type->elements_ = DYNAMIC_ARRAY_SIZE; + type->levels_ = 1; + return type; +} + +ReferenceType * +ReferenceType::New(Zone *zone, Handle contained) +{ + Local type(zone, (ReferenceType *)zone->allocate(MapKind_ReferenceType, sizeof(ReferenceType), Heap::Tenure_Old)); + if (!type) + return NULL; + + type->init(REFERENCE); + type->contained_ = contained; + return type; +} + +EnumType * +EnumType::New(Zone *zone, Handle name) +{ + Local type(zone, (EnumType *)zone->allocate(MapKind_EnumType, sizeof(EnumType), Heap::Tenure_Old)); + if (!type) + return NULL; + + type->init(ENUM); + type->name_ = name; + type->fields_ = NULL; + return type; +} + +StructType * +StructType::New(Zone *zone, Handle name, Handle fields) +{ + Local type(zone, (StructType *)zone->allocate(MapKind_StructType, sizeof(StructType), Heap::Tenure_Old)); + if (!type) + return NULL; + + type->init(STRUCT); + type->newMap_ = NULL; + type->fields_ = fields; + type->name_ = name; + + // Fill in field offsets. + StructureBufferBuilder builder; + Local fieldType(zone); + for (size_t i = 0; i < fields->length(); i++) { + Descriptor *desc = Descriptor::cast(fields->at(i)); + fieldType = desc->type(); + + unsigned offset; + if (!builder.add(fieldType, &offset)) + return NULL; + desc->setOffset(offset); + } + + Local map(zone, StructMap::New(zone, builder, type)); + if (!map) + return NULL; + + type->newMap_ = map; + + return type; +} + +Descriptor * +StructType::lookupField(String *name, unsigned *index) +{ + // :TODO: assert no GC + for (unsigned i = 0; i < fields_->length(); i++) { + Descriptor *desc = Descriptor::cast(fields_->at(i)); + if (desc->name() == name) { + if (index) + *index = i; + return desc; + } + } + + return NULL; +} + +FunctionType * +FunctionType::New(Zone *zone, Handle name, TokenKind token) +{ + Local type(zone, (FunctionType *)zone->allocate(MapKind_FunctionType, sizeof(FunctionType), Heap::Tenure_Old)); + if (!type) + return NULL; + + type->init(FUNCTION); + type->parameters_ = NULL; + type->defaults_ = NULL; + type->returnType_ = NULL; + type->name_ = name; + type->token_ = token; + return type; +} + +static size_t +FormatVA(char *buffer, size_t maxlen, const char *text, va_list ap) +{ + size_t len = vsnprintf(buffer, maxlen, text, ap); + if (len >= maxlen) { + buffer[maxlen - 1] = '\0'; + return maxlen - 1; + } + return len; +} + +static size_t +Format(char *buffer, size_t maxlen, const char *text, ...) +{ + va_list ap; + va_start(ap, text); + size_t len = FormatVA(buffer, maxlen, text, ap); + va_end(ap); + return len; +} + +static size_t +FormatTypeName(Type *type, char *buffer, size_t maxlen) +{ + if (type->isArray()) { + ArrayType *atype = ArrayType::cast(type); + Type *innermost = atype->innermost(); + + size_t len = FormatTypeName(innermost, buffer, maxlen); + for (;;) { + if (atype->isFixedLength()) + len += Format(buffer + len, maxlen - len, "[%d]", atype->fixedLength()); + else + len += Format(buffer + len, maxlen - len, "[]"); + if (!atype->contained()->isArray()) + break; + atype = ArrayType::cast(atype->contained()); + } + return len; + } + if (type->hasTypeQualifiers()) { + size_t len = 0; + if (type->quals() & TypeQual_Const) + len += Format(buffer, maxlen, "const "); + return len + FormatTypeName(type->unqualified(), buffer + len, maxlen - len); + } + if (type->isFunction()) + return Format(buffer, maxlen, "function"); + if (type->isVoid()) + return Format(buffer, maxlen, "void"); + if (type->isUnchecked()) + return Format(buffer, maxlen, "unchecked"); + if (type->isImportable()) + return Format(buffer, maxlen, "import"); + if (type->isEnum()) + return Format(buffer, maxlen, "%s", EnumType::cast(type)->name()->chars()); + if (type->isStruct()) + return Format(buffer, maxlen, "%s", StructType::cast(type)->name()->chars()); + if (type->isReference()) { + ReferenceType *ref = ReferenceType::cast(type); + size_t len = Format(buffer, maxlen, "&"); + return len + FormatTypeName(ref->contained(), buffer + len, maxlen - len); + } + switch (type->primitive()) { + case PrimitiveType_Bool: + return Format(buffer, maxlen, "bool"); + case PrimitiveType_Char: + return Format(buffer, maxlen, "char"); + case PrimitiveType_Int32: + return Format(buffer, maxlen, "int"); + case PrimitiveType_Float: + return Format(buffer, maxlen, "float"); + default: + return Format(buffer, maxlen, ""); + } +} + +const char * +ke::GetTypeName(Type *type, char *buffer, size_t maxlen) +{ + FormatTypeName(type, buffer, maxlen); + return buffer; +} diff -r c4b1341297e5 -r ba85a47ee414 src/Zone.cpp --- a/src/Zone.cpp Mon Jun 10 00:46:29 2013 +0100 +++ b/src/Zone.cpp Mon Jun 10 01:04:43 2013 +0100 @@ -1,368 +1,368 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of JITCraft. - * - * JITCraft is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * JITCraft. If not, see http://www.gnu.org/licenses/. - */ -#include -#include "Zone.h" -#include "StringPool.h" -#include "TypeManager.h" -#include "GlobalRoots.h" -#include "AllocatorPolicies.h" -#include "Array.h" -#include "NativeTable.h" -#include "Packages.h" -#include "Spaces-inl.h" -#include "Heap-inl.h" - -using namespace ke; - -Zone *ke::SingleThreadedZone; - -void * const NullAddress::Sentinel = NULL; - -Zone::Zone() - : stack_(thisFromCtor()), - heap_(thisFromCtor()), - types_(NULL), - symbols_(NULL), - globalRoots_(NULL), - nativeTable_(NULL), - packages_(NULL), - lastLocalRoot_(NULL), - specialRoots_(NULL), - hasPendingException_(false), - reporter_(NULL) -{ -} - -Zone::~Zone() -{ - delete nativeTable_; - delete types_; - delete symbols_; - delete packages_; - delete globalRoots_; -} - -bool -Zone::initialize() -{ - if (!stack_.initialize()) - return false; - if (!heap_.initialize()) - return false; - - symbols_ = new StringPool(this); - if (!symbols_ || !symbols_->initialize()) - return false; - - globalRoots_ = new GlobalRoots(this); - if (!globalRoots_ || !globalRoots_->initialize()) - return false; - - nativeTable_ = new NativeTable(this); - if (!nativeTable_ || !nativeTable_->initialize()) - return false; - - packages_ = new PackageCache(this); - if (!packages_ || !packages_->initialize()) - return false; - - MapKind maps[] = - { - MapKind_Type, - MapKind_ArrayType, - MapKind_FunctionType, - MapKind_StructType, - MapKind_ReferenceType, - MapKind_EnumType, - MapKind_FixedArray, - MapKind_ByteArray, - MapKind_Code, - MapKind_Reference, - MapKind_MetaArray, - MapKind_MetaStruct, - MapKind_MetaEnvironment, - MapKind_Module, - MapKind_Function, - MapKind_String, - MapKind_Native, - MapKind_Descriptor, - MapKind_Box, - MapKind_List, - MapKind_Package, - MapKind_FreeRegion, - MapKind_EnvironmentSlotDescriptorArray - }; - - maps_[MapKind_Meta] = heap_.metaMap(); - for (size_t i = 0; i < sizeof(maps) / sizeof(maps[0]); i++) { - if ((maps_[maps[i]] = allocateMap(maps[i], sizeof(Map))) == NULL) - return false; - } - - types_ = new TypeManager(this); - if (!types_ || !types_->initialize()) - return false; - - // Create the cached array maps for generic arrays. This is for the API - // version of Invoke(), which needs to make arrays very quickly from C++. - { - // Generic "cell" array (contents do not matter, just non-gc things). - Local type(this, types_->getPrimitive(PrimitiveType_Int32)); - if (!type) - return false; - - type = types_->newExternalArray(type); - if (!type) - return false; - - // :TODO: -#if 0 - externalGenericArrayMap_ = ArrayMap::Attach(this, type); - if (!externalGenericArrayMap_) - return false; -#endif - - type = types_->getPrimitive(PrimitiveType_Char); - if (!type) - return false; - - type = types_->newExternalArray(type); - if (!type) - return false; - - // :TODO: -#if 0 - externalCharArrayMap_ = ArrayMap::Attach(this, type); - if (!externalCharArrayMap_) - return false; -#endif - } - - return true; -} - -Map * -Zone::allocateMap(MapKind kind, size_t bytes, MapKind parent) -{ - assert(maps_[parent]); - Map *map = heap_.allocateMap(kind, bytes); - if (!map) - return NULL; - map->init(maps_[parent]); - return map; -} - -void -Zone::accept(VirtualObjectVisitor *visitor) -{ - for (unsigned i = 0; i < MapKinds_Total; i++) - visitor->visit(maps_[i].address()); - visitor->visit(externalCharArrayMap_.address()); - visitor->visit(externalGenericArrayMap_.address()); - types()->accept(visitor); - - Local *local = lastLocalRoot_; - while (local) { - visitor->visit(local->address()); - local = local->prev(); - } - - globalRoots_->accept(visitor); - rootScopes_.accept(visitor); - nativeTable_->accept(visitor); - packages_->accept(visitor); - - stack_.accept(visitor); -} - -void -Zone::sweepWeakRoots() -{ - symbols_->sweep(); - types()->sweep(); -} - -String * -Zone::makeSymbol(const char *str, size_t length) -{ - return symbols_->add(str, length); -} - -String * -Zone::makeSymbol(const char *str) -{ - return makeSymbol(str, strlen(str)); -} - -bool -Zone::hasPendingException() -{ - return hasPendingException_; -} - -void -Zone::reportOutOfMemory() -{ - -} - -void -Zone::reportError(Message message, ...) -{ - va_list ap; - va_start(ap, message); - reportErrorVA(message, ap); - va_end(ap); -} - -#if defined(_MSC_VER) -# define VA_COPY(to, from) to = from -#else -# define VA_COPY(to, from) va_copy(to, from) -#endif - -char * -ke::BuildErrorMessage(const char *format, va_list ap) -{ - va_list use; - VA_COPY(use, ap); - - // We over-allocate and zero terminate ahead of time, since LIBCRT's - // version of vsnprintf is screwy. - size_t size = 255; - char *buffer = (char *)calloc(size + 1, 1); - if (!buffer) - return NULL; - - for (;;) { - int r = vsnprintf(buffer, size, format, use); - - if (r > -1 && size_t(r) < size) - return buffer; - -#if defined(_MSC_VER) - if (r < 0) - size *= 2; -#else - if (r > -1) - size = size_t(r) + 1; - else - size *= 2; -#endif - - free(buffer); - if ((buffer = (char *)calloc(size + 1, 1)) == NULL) - return NULL; - - VA_COPY(use, ap); - } -} - -void -ke::FreeErrorMessage(const char *message) -{ - free((void *)message); -} - -void -Zone::reportErrorVA(Message message, va_list ap) -{ - char *report = BuildErrorMessage(Messages[message].format, ap); - if (!report) { - reportOutOfMemory(); - return; - } - - if (reporter_) { - // Reporters can re-enter. When we start having actual exception - // objects that can be caught and inspected, this code will have to - // save and restore the exception state. - reporter_->onUncaughtException(report); - } - - hasPendingException_ = true; - - FreeErrorMessage(report); -} - -void -Zone::installErrorReporter(Ke::IMessageReporter *reporter) -{ - reporter_ = reporter; -} - -void -ZoneAllocatorPolicy::reportOutOfMemory() -{ - ZONE()->reportOutOfMemory(); -} - -void -ZoneAllocatorPolicy::reportAllocationOverflow() -{ - ZONE()->reportAllocationOverflow(); -} - -void -Zone::reportAllocationOverflow() -{ - reportError(Message_AllocationOverflow); -} - -void -Zone::clearPendingException() -{ - hasPendingException_ = false; -} - -void -Zone::setPublicFunctionListener(Ke::IPublicFunctionListener *listener) -{ - publicFunctionListener_ = listener; -} - -void * -Zone::malloc(size_t size) -{ - void *ptr = ::malloc(size); - if (!ptr) - reportOutOfMemory(); - return ptr; -} - -void * -Zone::realloc(void *ptr, size_t size) -{ - ptr = ::realloc(ptr, size); - if (!ptr) - reportOutOfMemory(); - return ptr; -} - -AutoRooted::AutoRooted(Zone *zone) - : zone_(zone), - prev_(*zone->lastSpecialRoot()) -{ - *zone->lastSpecialRoot() = this; -} - -AutoRooted::~AutoRooted() -{ - *zone_->lastSpecialRoot() = prev_; -} +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include +#include "Zone.h" +#include "StringPool.h" +#include "TypeManager.h" +#include "GlobalRoots.h" +#include "AllocatorPolicies.h" +#include "Array.h" +#include "NativeTable.h" +#include "Packages.h" +#include "Spaces-inl.h" +#include "Heap-inl.h" + +using namespace ke; + +Zone *ke::SingleThreadedZone; + +void * const NullAddress::Sentinel = NULL; + +Zone::Zone() + : stack_(thisFromCtor()), + heap_(thisFromCtor()), + types_(NULL), + symbols_(NULL), + globalRoots_(NULL), + nativeTable_(NULL), + packages_(NULL), + lastLocalRoot_(NULL), + specialRoots_(NULL), + hasPendingException_(false), + reporter_(NULL) +{ +} + +Zone::~Zone() +{ + delete nativeTable_; + delete types_; + delete symbols_; + delete packages_; + delete globalRoots_; +} + +bool +Zone::initialize() +{ + if (!stack_.initialize()) + return false; + if (!heap_.initialize()) + return false; + + symbols_ = new StringPool(this); + if (!symbols_ || !symbols_->initialize()) + return false; + + globalRoots_ = new GlobalRoots(this); + if (!globalRoots_ || !globalRoots_->initialize()) + return false; + + nativeTable_ = new NativeTable(this); + if (!nativeTable_ || !nativeTable_->initialize()) + return false; + + packages_ = new PackageCache(this); + if (!packages_ || !packages_->initialize()) + return false; + + MapKind maps[] = + { + MapKind_Type, + MapKind_ArrayType, + MapKind_FunctionType, + MapKind_StructType, + MapKind_ReferenceType, + MapKind_EnumType, + MapKind_FixedArray, + MapKind_ByteArray, + MapKind_Code, + MapKind_Reference, + MapKind_MetaArray, + MapKind_MetaStruct, + MapKind_MetaEnvironment, + MapKind_Module, + MapKind_Function, + MapKind_String, + MapKind_Native, + MapKind_Descriptor, + MapKind_Box, + MapKind_List, + MapKind_Package, + MapKind_FreeRegion, + MapKind_EnvironmentSlotDescriptorArray + }; + + maps_[MapKind_Meta] = heap_.metaMap(); + for (size_t i = 0; i < sizeof(maps) / sizeof(maps[0]); i++) { + if ((maps_[maps[i]] = allocateMap(maps[i], sizeof(Map))) == NULL) + return false; + } + + types_ = new TypeManager(this); + if (!types_ || !types_->initialize()) + return false; + + // Create the cached array maps for generic arrays. This is for the API + // version of Invoke(), which needs to make arrays very quickly from C++. + { + // Generic "cell" array (contents do not matter, just non-gc things). + Local type(this, types_->getPrimitive(PrimitiveType_Int32)); + if (!type) + return false; + + type = types_->newExternalArray(type); + if (!type) + return false; + + // :TODO: +#if 0 + externalGenericArrayMap_ = ArrayMap::Attach(this, type); + if (!externalGenericArrayMap_) + return false; +#endif + + type = types_->getPrimitive(PrimitiveType_Char); + if (!type) + return false; + + type = types_->newExternalArray(type); + if (!type) + return false; + + // :TODO: +#if 0 + externalCharArrayMap_ = ArrayMap::Attach(this, type); + if (!externalCharArrayMap_) + return false; +#endif + } + + return true; +} + +Map * +Zone::allocateMap(MapKind kind, size_t bytes, MapKind parent) +{ + assert(maps_[parent]); + Map *map = heap_.allocateMap(kind, bytes); + if (!map) + return NULL; + map->init(maps_[parent]); + return map; +} + +void +Zone::accept(VirtualObjectVisitor *visitor) +{ + for (unsigned i = 0; i < MapKinds_Total; i++) + visitor->visit(maps_[i].address()); + visitor->visit(externalCharArrayMap_.address()); + visitor->visit(externalGenericArrayMap_.address()); + types()->accept(visitor); + + Local *local = lastLocalRoot_; + while (local) { + visitor->visit(local->address()); + local = local->prev(); + } + + globalRoots_->accept(visitor); + rootScopes_.accept(visitor); + nativeTable_->accept(visitor); + packages_->accept(visitor); + + stack_.accept(visitor); +} + +void +Zone::sweepWeakRoots() +{ + symbols_->sweep(); + types()->sweep(); +} + +String * +Zone::makeSymbol(const char *str, size_t length) +{ + return symbols_->add(str, length); +} + +String * +Zone::makeSymbol(const char *str) +{ + return makeSymbol(str, strlen(str)); +} + +bool +Zone::hasPendingException() +{ + return hasPendingException_; +} + +void +Zone::reportOutOfMemory() +{ + +} + +void +Zone::reportError(Message message, ...) +{ + va_list ap; + va_start(ap, message); + reportErrorVA(message, ap); + va_end(ap); +} + +#if defined(_MSC_VER) +# define VA_COPY(to, from) to = from +#else +# define VA_COPY(to, from) va_copy(to, from) +#endif + +char * +ke::BuildErrorMessage(const char *format, va_list ap) +{ + va_list use; + VA_COPY(use, ap); + + // We over-allocate and zero terminate ahead of time, since LIBCRT's + // version of vsnprintf is screwy. + size_t size = 255; + char *buffer = (char *)calloc(size + 1, 1); + if (!buffer) + return NULL; + + for (;;) { + int r = vsnprintf(buffer, size, format, use); + + if (r > -1 && size_t(r) < size) + return buffer; + +#if defined(_MSC_VER) + if (r < 0) + size *= 2; +#else + if (r > -1) + size = size_t(r) + 1; + else + size *= 2; +#endif + + free(buffer); + if ((buffer = (char *)calloc(size + 1, 1)) == NULL) + return NULL; + + VA_COPY(use, ap); + } +} + +void +ke::FreeErrorMessage(const char *message) +{ + free((void *)message); +} + +void +Zone::reportErrorVA(Message message, va_list ap) +{ + char *report = BuildErrorMessage(Messages[message].format, ap); + if (!report) { + reportOutOfMemory(); + return; + } + + if (reporter_) { + // Reporters can re-enter. When we start having actual exception + // objects that can be caught and inspected, this code will have to + // save and restore the exception state. + reporter_->onUncaughtException(report); + } + + hasPendingException_ = true; + + FreeErrorMessage(report); +} + +void +Zone::installErrorReporter(Ke::IMessageReporter *reporter) +{ + reporter_ = reporter; +} + +void +ZoneAllocatorPolicy::reportOutOfMemory() +{ + ZONE()->reportOutOfMemory(); +} + +void +ZoneAllocatorPolicy::reportAllocationOverflow() +{ + ZONE()->reportAllocationOverflow(); +} + +void +Zone::reportAllocationOverflow() +{ + reportError(Message_AllocationOverflow); +} + +void +Zone::clearPendingException() +{ + hasPendingException_ = false; +} + +void +Zone::setPublicFunctionListener(Ke::IPublicFunctionListener *listener) +{ + publicFunctionListener_ = listener; +} + +void * +Zone::malloc(size_t size) +{ + void *ptr = ::malloc(size); + if (!ptr) + reportOutOfMemory(); + return ptr; +} + +void * +Zone::realloc(void *ptr, size_t size) +{ + ptr = ::realloc(ptr, size); + if (!ptr) + reportOutOfMemory(); + return ptr; +} + +AutoRooted::AutoRooted(Zone *zone) + : zone_(zone), + prev_(*zone->lastSpecialRoot()) +{ + *zone->lastSpecialRoot() = this; +} + +AutoRooted::~AutoRooted() +{ + *zone_->lastSpecialRoot() = prev_; +} diff -r c4b1341297e5 -r ba85a47ee414 src/Zone.h --- a/src/Zone.h Mon Jun 10 00:46:29 2013 +0100 +++ b/src/Zone.h Mon Jun 10 01:04:43 2013 +0100 @@ -1,221 +1,221 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#ifndef _include_jitcraft_thread_h_ -#define _include_jitcraft_thread_h_ - -#include -#include "PoolAllocator.h" -#include "Stack.h" -#include "Heap.h" -#include "Barriers.h" -#include "Handles.h" -#include "Messages.h" -#include "RootScopes.h" -#include "VirtualObjectVisitor.h" - -namespace ke { - -class TypeManager; -class String; -class StringPool; -class GlobalRoots; -class ArrayMap; -class Exception; -class NativeTable; -class PackageCache; - -class Zone -{ - PoolAllocator pool_; - Stack stack_; - Heap heap_; - - // Map cache. - Unbarriered maps_[MapKinds_Total]; - Unbarriered externalGenericArrayMap_; - Unbarriered externalCharArrayMap_; - - TypeManager *types_; - StringPool *symbols_; - GlobalRoots *globalRoots_; - NativeTable *nativeTable_; - PackageCache *packages_; - - // Root list. - Local *lastLocalRoot_; - RootScopeManager rootScopes_; - AutoRooted *specialRoots_; - - // Exception stuff. - bool hasPendingException_; - Ke::IMessageReporter *reporter_; - - Ke::IPublicFunctionListener *publicFunctionListener_; - - private: - Zone *thisFromCtor() { - return this; - } - - public: - Zone(); - ~Zone(); - bool initialize(); - - PoolAllocator &pool() { - return pool_; - } - Stack &stack() { - return stack_; - } - Heap &heap() { - return heap_; - } - Object *allocate(MapKind kind, size_t bytes, Heap::Tenure tenure = Heap::Tenure_Default) { - assert(maps_[kind]); - Local map(this, maps_[kind]); - return allocate(map, bytes, tenure); - } - Object *allocate(Handle map, size_t bytes, Heap::Tenure tenure = Heap::Tenure_Default) { - return heap_.allocate(map, bytes, tenure); - } - Map *getMap(MapKind kind) { - return maps_[kind]; - } - Map *allocateMap(MapKind kind, size_t bytes, MapKind parent = MapKind_Meta); - - ArrayMap *externalGenericArrayMap() { - return externalGenericArrayMap_; - } - ArrayMap *externalCharArrayMap() { - return externalCharArrayMap_; - } - - TypeManager *types() const { - return types_; - } - AutoRooted **lastSpecialRoot() { - return &specialRoots_; - } - Local **lastLocalRoot() { - return &lastLocalRoot_; - } - Object **makeScopedRoot(Object *ptr) { - return rootScopes_.make(ptr); - } - RootScopeManager &rootScopes() { - return rootScopes_; - } - StringPool *symbols() { - return symbols_; - } - GlobalRoots *globalRoots() { - return globalRoots_; - } - NativeTable *nativeTable() { - return nativeTable_; - } - PackageCache *packages() { - return packages_; - } - - String *makeSymbol(const char *string, size_t length); - String *makeSymbol(const char *string); - - void reportOutOfMemory(); - void reportAllocationOverflow(); - void reportError(Message message, ...); - void reportErrorVA(Message message, va_list ap); - bool hasPendingException(); - void clearPendingException(); - void installErrorReporter(Ke::IMessageReporter *reporter); - Ke::IMessageReporter *errorReporter() { - return reporter_; - } - - void accept(VirtualObjectVisitor *visitor); - void sweepWeakRoots(); - - void setPublicFunctionListener(Ke::IPublicFunctionListener *listener); - void registerPublic(Handle fun); - void unregisterPublic(Handle fun); - - // Helpers that report OOM. - void *malloc(size_t size); - void *realloc(void *ptr, size_t size); -}; - -// In a multi-threaded system, this would be a TLS variable. -// In the interest of simplicity, we are single-threaded for now. -extern Zone *SingleThreadedZone; - -// Exposed for the compiler. -char *BuildErrorMessage(const char *format, va_list ap); -void FreeErrorMessage(const char *message); - -static inline Zone *ZONE() -{ - return SingleThreadedZone; -} - -template -Local::Local(Zone *zone, T *thing) - : zone_(zone), - thing_(thing), - prev_(*zone->lastLocalRoot()) -{ - *zone->lastLocalRoot() = toRaw(); -} - -template - -Local::~Local() -{ - assert(*zone_->lastLocalRoot() == toRaw()); - *zone_->lastLocalRoot() = prev_; -} - -template -ScopedRoot::ScopedRoot() - : thingp_(make(NULL)) -{ -} - -template -ScopedRoot::ScopedRoot(T *thing) - : thingp_(make(thing)) -{ - // Type check that T is an Object. - (void)static_cast(thing); -} - -template T ** -ScopedRoot::make(T *other) -{ - return reinterpret_cast(ZONE()->makeScopedRoot(other)); -} - -RootScope::RootScope(Zone *zone) - : Ke::HandleScope(zone, zone->rootScopes().position(), *zone->rootScopes().scope()) -{ -} - -} - -#endif // _include_jitcraft_thread_h_ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_thread_h_ +#define _include_jitcraft_thread_h_ + +#include +#include "PoolAllocator.h" +#include "Stack.h" +#include "Heap.h" +#include "Barriers.h" +#include "Handles.h" +#include "Messages.h" +#include "RootScopes.h" +#include "VirtualObjectVisitor.h" + +namespace ke { + +class TypeManager; +class String; +class StringPool; +class GlobalRoots; +class ArrayMap; +class Exception; +class NativeTable; +class PackageCache; + +class Zone +{ + PoolAllocator pool_; + Stack stack_; + Heap heap_; + + // Map cache. + Unbarriered maps_[MapKinds_Total]; + Unbarriered externalGenericArrayMap_; + Unbarriered externalCharArrayMap_; + + TypeManager *types_; + StringPool *symbols_; + GlobalRoots *globalRoots_; + NativeTable *nativeTable_; + PackageCache *packages_; + + // Root list. + Local *lastLocalRoot_; + RootScopeManager rootScopes_; + AutoRooted *specialRoots_; + + // Exception stuff. + bool hasPendingException_; + Ke::IMessageReporter *reporter_; + + Ke::IPublicFunctionListener *publicFunctionListener_; + + private: + Zone *thisFromCtor() { + return this; + } + + public: + Zone(); + ~Zone(); + bool initialize(); + + PoolAllocator &pool() { + return pool_; + } + Stack &stack() { + return stack_; + } + Heap &heap() { + return heap_; + } + Object *allocate(MapKind kind, size_t bytes, Heap::Tenure tenure = Heap::Tenure_Default) { + assert(maps_[kind]); + Local map(this, maps_[kind]); + return allocate(map, bytes, tenure); + } + Object *allocate(Handle map, size_t bytes, Heap::Tenure tenure = Heap::Tenure_Default) { + return heap_.allocate(map, bytes, tenure); + } + Map *getMap(MapKind kind) { + return maps_[kind]; + } + Map *allocateMap(MapKind kind, size_t bytes, MapKind parent = MapKind_Meta); + + ArrayMap *externalGenericArrayMap() { + return externalGenericArrayMap_; + } + ArrayMap *externalCharArrayMap() { + return externalCharArrayMap_; + } + + TypeManager *types() const { + return types_; + } + AutoRooted **lastSpecialRoot() { + return &specialRoots_; + } + Local **lastLocalRoot() { + return &lastLocalRoot_; + } + Object **makeScopedRoot(Object *ptr) { + return rootScopes_.make(ptr); + } + RootScopeManager &rootScopes() { + return rootScopes_; + } + StringPool *symbols() { + return symbols_; + } + GlobalRoots *globalRoots() { + return globalRoots_; + } + NativeTable *nativeTable() { + return nativeTable_; + } + PackageCache *packages() { + return packages_; + } + + String *makeSymbol(const char *string, size_t length); + String *makeSymbol(const char *string); + + void reportOutOfMemory(); + void reportAllocationOverflow(); + void reportError(Message message, ...); + void reportErrorVA(Message message, va_list ap); + bool hasPendingException(); + void clearPendingException(); + void installErrorReporter(Ke::IMessageReporter *reporter); + Ke::IMessageReporter *errorReporter() { + return reporter_; + } + + void accept(VirtualObjectVisitor *visitor); + void sweepWeakRoots(); + + void setPublicFunctionListener(Ke::IPublicFunctionListener *listener); + void registerPublic(Handle fun); + void unregisterPublic(Handle fun); + + // Helpers that report OOM. + void *malloc(size_t size); + void *realloc(void *ptr, size_t size); +}; + +// In a multi-threaded system, this would be a TLS variable. +// In the interest of simplicity, we are single-threaded for now. +extern Zone *SingleThreadedZone; + +// Exposed for the compiler. +char *BuildErrorMessage(const char *format, va_list ap); +void FreeErrorMessage(const char *message); + +static inline Zone *ZONE() +{ + return SingleThreadedZone; +} + +template +Local::Local(Zone *zone, T *thing) + : zone_(zone), + thing_(thing), + prev_(*zone->lastLocalRoot()) +{ + *zone->lastLocalRoot() = toRaw(); +} + +template + +Local::~Local() +{ + assert(*zone_->lastLocalRoot() == toRaw()); + *zone_->lastLocalRoot() = prev_; +} + +template +ScopedRoot::ScopedRoot() + : thingp_(make(NULL)) +{ +} + +template +ScopedRoot::ScopedRoot(T *thing) + : thingp_(make(thing)) +{ + // Type check that T is an Object. + (void)static_cast(thing); +} + +template T ** +ScopedRoot::make(T *other) +{ + return reinterpret_cast(ZONE()->makeScopedRoot(other)); +} + +RootScope::RootScope(Zone *zone) + : Ke::HandleScope(zone, zone->rootScopes().position(), *zone->rootScopes().scope()) +{ +} + +} + +#endif // _include_jitcraft_thread_h_ diff -r c4b1341297e5 -r ba85a47ee414 src/compiler/AST.h --- a/src/compiler/AST.h Mon Jun 10 00:46:29 2013 +0100 +++ b/src/compiler/AST.h Mon Jun 10 01:04:43 2013 +0100 @@ -1,1169 +1,1169 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#ifndef _include_sourcepawn_ast_h_ -#define _include_sourcepawn_ast_h_ - -#include "../PoolAllocator.h" -#include "../Vector.h" -#include "Token.h" -#include "NameBinding.h" -#include "Symbols.h" - -namespace ke { - -class Scope; -class Importable; -struct PathComponent; - -#define ASTKINDS(_) \ - _(VariableDeclaration) \ - _(ForStatement) \ - _(ReturnStatement) \ - _(IntegerLiteral) \ - _(FloatLiteral) \ - _(StringLiteral) \ - _(BinaryExpression) \ - _(BlockStatement) \ - _(Assignment) \ - _(NameProxy) \ - _(ExpressionStatement) \ - _(FunctionStatement) \ - _(CallExpression) \ - _(IfStatement) \ - _(IndexExpression) \ - _(FieldExpression) \ - _(EnumStatement) \ - _(WhileStatement) \ - _(BreakStatement) \ - _(ContinueStatement) \ - _(FunctionTypeStatement) \ - _(IncDecExpression) \ - _(UnaryExpression) \ - _(TernaryExpression) \ - _(BooleanLiteral) \ - _(StructureStatement) \ - _(SwitchStatement) \ - _(ArrayLiteral) \ - _(ImportStatement) - -// Forward declarations. -#define _(name) class name; -ASTKINDS(_) -#undef _ - -class AstVisitor; - -// Interface for AST nodes. -class AstNode : public PoolObject -{ - SourcePosition pos_; - - public: - enum Kind { -# define _(name) k##name, - ASTKINDS(_) -# undef _ - AstKind_Invalid - }; - - AstNode(const SourcePosition &pos) - : pos_(pos) - { - } - - virtual Kind kind() const = 0; - -#define _(name) bool is##name() { return kind() == k##name; } \ - name *to##name() { assert(is##name()); return (name *)this; } \ - name *as##name() { if (!is##name()) return NULL; return to##name(); } - ASTKINDS(_) -#undef _ - - virtual void accept(AstVisitor *visitor) = 0; - - const SourcePosition &pos() const { - return pos_; - } -}; - -#define DECLARE_NODE(type) \ - Kind kind() const { \ - return k##type; \ - } \ - void accept(AstVisitor *visitor) { \ - visitor->visit##type(this); \ - } - -class AstVisitor -{ - public: -#define _(name) virtual void visit##name(name *node) { assert(false); } - ASTKINDS(_) -#undef _ -}; - -class Statement : public AstNode -{ - public: - Statement(const SourcePosition &pos) - : AstNode(pos) - { - } -}; - -class Expression : public AstNode -{ - public: - Expression(const SourcePosition &pos) - : AstNode(pos) - { - } -}; - -typedef PoolList StatementList; -typedef PoolList ExpressionList; - -class VariableDeclaration : public Statement -{ - TypeQualifiers qual_; - ScopedRoot name_; - Expression *initialization_; - ExpressionList *dims_; - Expression *type_; - VariableSymbol *sym_; - - public: - VariableDeclaration(const SourcePosition &pos, TypeQualifiers qual, Handle name, - Expression *type, ExpressionList *dims, Expression *initialization) - : Statement(pos), - qual_(qual), - name_(name), - initialization_(initialization), - dims_(dims), - type_(type), - sym_(NULL) - { - } - - DECLARE_NODE(VariableDeclaration); - - Expression *initialization() const { - return initialization_; - } - Handle name() { - return name_; - } - Expression *declType() const { - return type_; - } - void setSymbol(VariableSymbol *sym) { - assert(!sym_); - sym_ = sym; - } - VariableSymbol *sym() const { - return sym_; - } - Expression *type() const { - return type_; - } - ExpressionList *dims() const { - return dims_; - } - TypeQualifiers quals() const { - return qual_; - } -}; - -class Parameter : public PoolObject -{ - ScopedRoot name_; - Expression *type_; - bool reference_; - Expression *initializer_; - VariableSymbol *sym_; - SourcePosition pos_; - ExpressionList *dims_; - TypeQualifiers quals_; - - public: - Parameter(Handle name, Expression *type, bool reference, TypeQualifiers quals, - ExpressionList *dims, Expression *initializer, const SourcePosition &pos) - : name_(name), - type_(type), - reference_(reference), - initializer_(initializer), - sym_(NULL), - pos_(pos), - dims_(dims), - quals_(quals) - { - } - - Expression *type() const { - return type_; - } - Handle name() const { - return name_; - } - bool reference() const { - return reference_; - } - TypeQualifiers quals() const { - return quals_; - } - ExpressionList *dims() const { - return dims_; - } - Expression *initializer() const { - return initializer_; - } - void setSymbol(VariableSymbol *sym) { - assert(!sym_); - sym_ = sym; - } - VariableSymbol *sym() const { - return sym_; - } - const SourcePosition &pos() const { - return pos_; - } -}; - -class NameProxy : public Expression -{ - ScopedRoot name_; - Symbol *binding_; - - public: - NameProxy(const SourcePosition &pos, Handle name) - : Expression(pos), - name_(name), - binding_(NULL) - { - } - - DECLARE_NODE(NameProxy); - - Handle name() const { - return name_; - } - Symbol *sym() const { - return binding_; - } - void bind(Symbol *sym) { - binding_ = sym; - } -}; - -class BooleanLiteral : public Expression -{ - TokenKind token_; - - public: - BooleanLiteral(const SourcePosition &pos, TokenKind token) - : Expression(pos), - token_(token) - { - } - - DECLARE_NODE(BooleanLiteral); - TokenKind token() const { - return token_; - } -}; - -class IntegerLiteral : public Expression -{ - int value_; - - public: - IntegerLiteral(const SourcePosition &pos, int value) - : Expression(pos), - value_(value) - { - } - - DECLARE_NODE(IntegerLiteral); - - int value() const { - return value_; - } -}; - -class FloatLiteral : public Expression -{ - double value_; - - public: - FloatLiteral(const SourcePosition &pos, double value) - : Expression(pos), - value_(value) - { - } - - DECLARE_NODE(FloatLiteral); - - double value() const { - return value_; - } -}; - -class StringLiteral : public Expression -{ - ScopedRoot string_; - - public: - StringLiteral(const SourcePosition &pos, String *string) - : Expression(pos), - string_(string) - { - } - - DECLARE_NODE(StringLiteral); - - String *string() const { - return string_; - } -}; - -class ArrayLiteral : public Expression -{ - TokenKind token_; - ExpressionList *expressions_; - - public: - ArrayLiteral(const SourcePosition &pos, TokenKind token, ExpressionList *expressions) - : Expression(pos), - token_(token), - expressions_(expressions) - { - } - - DECLARE_NODE(ArrayLiteral); - - TokenKind token() const { - return token_; - } - ExpressionList *expressions() const { - return expressions_; - } - bool isFixed() const { - return token_ == TOK_LBRACE; - } -}; - -class UnaryExpression : public Expression -{ - Expression *expression_; - TokenKind token_; - NameProxy *tag_; - - public: - UnaryExpression(const SourcePosition &pos, TokenKind token, Expression *expr) - : Expression(pos), - expression_(expr), - token_(token), - tag_(NULL) - { - } - - UnaryExpression(const SourcePosition &pos, TokenKind token, Expression *expr, - NameProxy *tag) - : Expression(pos), - expression_(expr), - token_(token), - tag_(tag) - { - } - - DECLARE_NODE(UnaryExpression); - - Expression *expression() const { - return expression_; - } - TokenKind token() const { - return token_; - } - NameProxy *tag() const { - return tag_; - } -}; - -class BinaryExpression : public Expression -{ - Expression *left_; - Expression *right_; - TokenKind token_; - - public: - BinaryExpression(const SourcePosition &pos, TokenKind token, Expression *left, Expression *right) - : Expression(pos), - left_(left), - right_(right), - token_(token) - { - } - - DECLARE_NODE(BinaryExpression); - - Expression *left() const { - return left_; - } - Expression *right() const { - return right_; - } - TokenKind token() const { - return token_; - } -}; - -class TernaryExpression : public Expression -{ - Expression *condition_; - Expression *left_; - Expression *right_; - - public: - TernaryExpression(const SourcePosition &pos, Expression *condition, Expression *left, Expression *right) - : Expression(pos), - condition_(condition), - left_(left), - right_(right) - { - } - - DECLARE_NODE(TernaryExpression); - - Expression *condition() const { - return condition_; - } - Expression *left() const { - return left_; - } - Expression *right() const { - return right_; - } -}; - -class IndexExpression : public Expression -{ - Expression *left_; - Expression *right_; - - public: - IndexExpression(const SourcePosition &pos, Expression *left, Expression *right) - : Expression(pos), - left_(left), - right_(right) - { - } - - DECLARE_NODE(IndexExpression); - - Expression *left() const { - return left_; - } - Expression *right() const { - return right_; - } -}; - -class FieldExpression : public Expression -{ - Expression *left_; - ScopedRoot field_; - - public: - FieldExpression(const SourcePosition &pos, Expression *left, Handle field) - : Expression(pos), - left_(left), - field_(field) - { - } - - DECLARE_NODE(FieldExpression); - - Expression *left() const { - return left_; - } - Handle field() const { - return field_; - } -}; - -class CallExpression : public Expression -{ - Expression *callee_; - ExpressionList *arguments_; - - public: - CallExpression(const SourcePosition &pos, Expression *callee, ExpressionList *arguments) - : Expression(pos), - callee_(callee), - arguments_(arguments) - { - } - - DECLARE_NODE(CallExpression); - - Expression *callee() const { - return callee_; - } - ExpressionList *arguments() const { - return arguments_; - } -}; - -class ForStatement : public Statement -{ - Statement *initialization_; - Expression *condition_; - Statement *update_; - Statement *body_; - Scope *scope_; - - public: - ForStatement(const SourcePosition &pos, Statement *initialization, - Expression *condition, Statement *update, Statement *body) - : Statement(pos), - initialization_(initialization), - condition_(condition), - update_(update), - body_(body), - scope_(NULL) - { - } - - DECLARE_NODE(ForStatement); - - Statement *initialization() const { - return initialization_; - } - Expression *condition() const { - return condition_; - } - Statement *update() const { - return update_; - } - Statement *body() const { - return body_; - } - Scope *scope() const { - return scope_; - } - void setScope(Scope *scope) { - scope_ = scope; - } -}; - -class WhileStatement : public Statement -{ - TokenKind token_; - Expression *condition_; - Statement *body_; - - public: - WhileStatement(const SourcePosition &pos, TokenKind kind, Expression *condition, Statement *body) - : Statement(pos), - token_(kind), - condition_(condition), - body_(body) - { - } - - DECLARE_NODE(WhileStatement); - - TokenKind token() const { - return token_; - } - Expression *condition() const { - return condition_; - } - Statement *body() const { - return body_; - } -}; - -class ReturnStatement : public Statement -{ - Expression *expression_; - - public: - ReturnStatement(const SourcePosition &pos, Expression *expression) - : Statement(pos), - expression_(expression) - { - } - - DECLARE_NODE(ReturnStatement); - - Expression *expression() const { - return expression_; - } -}; - -class BreakStatement : public Statement -{ - public: - BreakStatement(const SourcePosition &pos) - : Statement(pos) - { - } - - DECLARE_NODE(BreakStatement); -}; - -class ContinueStatement : public Statement -{ - public: - ContinueStatement(const SourcePosition &pos) - : Statement(pos) - { - } - - DECLARE_NODE(ContinueStatement); -}; - -class Assignment : public Expression -{ - TokenKind token_; - Expression *lvalue_; - Expression *expression_; - - public: - Assignment(const SourcePosition &pos, TokenKind token, Expression *left, Expression *right) - : Expression(pos), - token_(token), - lvalue_(left), - expression_(right) - { - } - - DECLARE_NODE(Assignment); - - TokenKind token() const { - return token_; - } - Expression *lvalue() const { - return lvalue_; - } - Expression *expression() const { - return expression_; - } -}; - -class ExpressionStatement : public Statement -{ - Expression *expression_; - - public: - ExpressionStatement(Expression *expression) - : Statement(expression->pos()), - expression_(expression) - { - } - - DECLARE_NODE(ExpressionStatement); - - Expression *expression() const { - return expression_; - } -}; - -class BlockStatement : public Statement -{ - StatementList *statements_; - Scope *scope_; - TokenKind type_; - - public: - BlockStatement(const SourcePosition &pos, StatementList *statements, TokenKind kind) - : Statement(pos), - statements_(statements), - scope_(NULL), - type_(kind) - { - } - - DECLARE_NODE(BlockStatement); - - StatementList *statements() const { - return statements_; - } - Scope *scope() const { - return scope_; - } - void setScope(Scope *scope) { - scope_ = scope; - } - TokenKind type() const { - return type_; - } -}; - -typedef PoolList ParameterList; - -class FunctionSignature -{ - public: - FunctionSignature(Expression *returnType, ParameterList *parameters, bool variadic) - : returnType_(returnType), - parameters_(parameters), - variadic_(variadic) - { - } - - Expression *returnType() const { - return returnType_; - } - ParameterList *parameters() const { - return parameters_; - } - bool variadic() const { - return variadic_; - } - - private: - Expression *returnType_; - ParameterList *parameters_; - bool variadic_; -}; - -class FunctionTypeStatement : public Statement -{ - public: - FunctionTypeStatement(const SourcePosition &pos, Handle name, const FunctionSignature &signature) - : Statement(pos), - name_(name), - signature_(signature) - { - } - - DECLARE_NODE(FunctionTypeStatement); - - Handle name() const { - return name_; - } - const FunctionSignature &signature() const { - return signature_; - } - - private: - ScopedRoot name_; - FunctionSignature signature_; - -}; - -class FunctionStatement : public Statement -{ - ScopedRoot name_; - TokenKind kind_; - Statement *body_; - FunctionSignature signature_; - bool explicitReturn_; - FunctionSymbol *sym_; - Scope *argScope_; - Scope *varScope_; - - public: - FunctionStatement(const SourcePosition &pos, Handle name, TokenKind kind, - Statement *body, const FunctionSignature &signature, bool explicitReturn) - : Statement(pos), - name_(name), - kind_(kind), - body_(body), - signature_(signature), - explicitReturn_(explicitReturn), - sym_(NULL), - argScope_(NULL), - varScope_(NULL) - { - } - - DECLARE_NODE(FunctionStatement); - - Handle name() const { - return name_; - } - Statement *body() const { - return body_; - } - const FunctionSignature &signature() { - return signature_; - } - bool hasExplicitReturn() const { - return explicitReturn_; - } - TokenKind token() const { - return kind_; - } - void setSymbol(FunctionSymbol *sym) { - assert(!sym_); - sym_ = sym; - } - FunctionSymbol *sym() const { - return sym_; - } - void setScopes(Scope *argScope, Scope *varScope) { - argScope_ = argScope; - varScope_ = varScope; - } - Scope *argScope() const { - return argScope_; - } - Scope *varScope() const { - return varScope_; - } -}; - -class IfStatement : public Statement -{ - Expression *condition_; - Statement *ifTrue_; - Statement *ifFalse_; - - public: - IfStatement(const SourcePosition &pos, Expression *condition, Statement *ifTrue) - : Statement(pos), - condition_(condition), - ifTrue_(ifTrue), - ifFalse_(NULL) - { - } - - DECLARE_NODE(IfStatement); - - Expression *condition() const { - return condition_; - } - Statement *ifTrue() const { - return ifTrue_; - } - Statement *ifFalse() const { - return ifFalse_; - } - void setIfFalse(Statement *ifFalse) { - ifFalse_ = ifFalse; - } -}; - -class EnumStatement : public Statement -{ - ScopedRoot name_; - TypeSymbol *sym_; - - public: - struct Entry { - ScopedRoot name; - Expression *expr; - SourcePosition pos; - ConstantSymbol *sym; - - Entry(Handle name, Expression *expr, const SourcePosition &pos) - : name(name), - expr(expr), - pos(pos), - sym(NULL) - { - } - }; - - typedef PoolList EntryList; - - public: - EnumStatement(const SourcePosition &pos, TokenKind visibility, Handle name, EntryList *entries) - : Statement(pos), - name_(name), - sym_(NULL), - entries_(entries), - visibility_(visibility) - { - } - - DECLARE_NODE(EnumStatement); - - Handle name() const { - return name_; - } - EntryList *entries() const { - return entries_; - } - void setSymbol(TypeSymbol *sym) { - assert(!sym_); - sym_ = sym; - } - TypeSymbol *sym() const { - return sym_; - } - TokenKind visibility() const { - return visibility_; - } - - private: - EntryList *entries_; - TokenKind visibility_; -}; - -class IncDecExpression : public Expression -{ - TokenKind token_; - Expression *expression_; - bool postfix_; - - public: - IncDecExpression(const SourcePosition &pos, TokenKind token, Expression *expression, bool postfix) - : Expression(pos), - token_(token), - expression_(expression), - postfix_(postfix) - { - } - - DECLARE_NODE(IncDecExpression); - - TokenKind token() const { - return token_; - } - Expression *expression() const { - return expression_; - } - bool postfix() const { - return postfix_; - } -}; - -class Case : public PoolObject -{ - public: - Case(Expression *expression, ExpressionList *others, Statement *statement) - : expression_(expression), - others_(others), - statement_(statement) - { - } - - Expression *expression() const { - return expression_; - } - PoolList *others() const { - return others_; - } - Statement *statement() const { - return statement_; - } - - private: - Expression *expression_; - PoolList *others_; - Statement *statement_; -}; - -struct CaseValue -{ - BoxedPrimitive box; - size_t statement; - - CaseValue(size_t statement) - : statement(statement) - { - } -}; - -typedef PoolList CaseValueList; - -class SwitchStatement : public Statement -{ - public: - SwitchStatement(const SourcePosition &pos, Expression *expression, PoolList *cases, - Statement *def) - : Statement(pos), - expression_(expression), - cases_(cases), - default_(def), - table_(NULL) - { - } - - DECLARE_NODE(SwitchStatement); - - Expression *expression() const { - return expression_; - } - PoolList *cases() const { - return cases_; - } - Statement *defaultCase() const { - return default_; - } - void setCaseValueList(CaseValueList *list) { - table_ = list; - } - CaseValueList *caseValueList() const { - return table_; - } - - private: - Expression *expression_; - PoolList *cases_; - Statement *default_; - CaseValueList *table_; -}; - -class StructureStatement : public Statement -{ - public: - class Field : public PoolObject - { - SourcePosition pos_; - Expression *type_; - ScopedRoot name_; - - public: - Field(const SourcePosition &pos, - Expression *type, - Handle name) - : pos_(pos), - type_(type), - name_(name) - { - } - - const SourcePosition &pos() const { - return pos_; - } - Handle name() const { - return name_; - } - Expression *type() const { - return type_; - } - }; - - typedef PoolList FieldList; - - public: - StructureStatement(const SourcePosition &pos, Handle name, FieldList *fields) - : Statement(pos), - name_(name), - fields_(fields), - sym_(NULL) - { - } - - DECLARE_NODE(StructureStatement); - - FieldList *fields() const { - return fields_; - } - Handle name() const { - return name_; - } - void setSymbol(TypeSymbol *sym) { - assert(!sym_); - sym_ = sym; - } - TypeSymbol *sym() const { - return sym_; - } - - private: - ScopedRoot name_; - FieldList *fields_; - TypeSymbol *sym_; -}; - -typedef PoolList *> NameList; - -class ImportStatement : public Statement -{ - public: - ImportStatement(const SourcePosition &pos, PathComponent *path, NameList *names) - : Statement(pos), - path_(path), - names_(names) - { - } - - DECLARE_NODE(ImportStatement); - - PathComponent *path() const { - return path_; - } - void setSource(Importable *source) { - source_ = source; - } - Handle source() { - return source_; - } - unsigned importIndex() const { - return importIndex_; - } - void setImportIndex(unsigned index) { - importIndex_ = index; - } - NameList *names() const { - return names_; - } - - bool importAllNames() const { - return names_ && names_->empty(); - } - - private: - PathComponent *path_; - ScopedRoot source_; - unsigned importIndex_; - NameList *names_; -}; - -typedef StructureStatement::FieldList FieldList; -typedef PoolList NameProxyList; - -#undef DECLARE_NODE - -class ParseTree : public PoolObject -{ - StatementList *statements_; - NameProxyList *unbound_; - PoolList *imports_; - - public: - ParseTree(StatementList *statements, NameProxyList *unbound, PoolList *imports) - : statements_(statements), - unbound_(unbound), - imports_(imports) - { - } - - void dump(FILE *fp); - - StatementList *statements() const { - return statements_; - } - NameProxyList *unbound() const { - return unbound_; - } - PoolList *imports() const { - return imports_; - } -}; - -} - -#endif // _include_sourcepawn_ast_h_ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_sourcepawn_ast_h_ +#define _include_sourcepawn_ast_h_ + +#include "../PoolAllocator.h" +#include "../Vector.h" +#include "Token.h" +#include "NameBinding.h" +#include "Symbols.h" + +namespace ke { + +class Scope; +class Importable; +struct PathComponent; + +#define ASTKINDS(_) \ + _(VariableDeclaration) \ + _(ForStatement) \ + _(ReturnStatement) \ + _(IntegerLiteral) \ + _(FloatLiteral) \ + _(StringLiteral) \ + _(BinaryExpression) \ + _(BlockStatement) \ + _(Assignment) \ + _(NameProxy) \ + _(ExpressionStatement) \ + _(FunctionStatement) \ + _(CallExpression) \ + _(IfStatement) \ + _(IndexExpression) \ + _(FieldExpression) \ + _(EnumStatement) \ + _(WhileStatement) \ + _(BreakStatement) \ + _(ContinueStatement) \ + _(FunctionTypeStatement) \ + _(IncDecExpression) \ + _(UnaryExpression) \ + _(TernaryExpression) \ + _(BooleanLiteral) \ + _(StructureStatement) \ + _(SwitchStatement) \ + _(ArrayLiteral) \ + _(ImportStatement) + +// Forward declarations. +#define _(name) class name; +ASTKINDS(_) +#undef _ + +class AstVisitor; + +// Interface for AST nodes. +class AstNode : public PoolObject +{ + SourcePosition pos_; + + public: + enum Kind { +# define _(name) k##name, + ASTKINDS(_) +# undef _ + AstKind_Invalid + }; + + AstNode(const SourcePosition &pos) + : pos_(pos) + { + } + + virtual Kind kind() const = 0; + +#define _(name) bool is##name() { return kind() == k##name; } \ + name *to##name() { assert(is##name()); return (name *)this; } \ + name *as##name() { if (!is##name()) return NULL; return to##name(); } + ASTKINDS(_) +#undef _ + + virtual void accept(AstVisitor *visitor) = 0; + + const SourcePosition &pos() const { + return pos_; + } +}; + +#define DECLARE_NODE(type) \ + Kind kind() const { \ + return k##type; \ + } \ + void accept(AstVisitor *visitor) { \ + visitor->visit##type(this); \ + } + +class AstVisitor +{ + public: +#define _(name) virtual void visit##name(name *node) { assert(false); } + ASTKINDS(_) +#undef _ +}; + +class Statement : public AstNode +{ + public: + Statement(const SourcePosition &pos) + : AstNode(pos) + { + } +}; + +class Expression : public AstNode +{ + public: + Expression(const SourcePosition &pos) + : AstNode(pos) + { + } +}; + +typedef PoolList StatementList; +typedef PoolList ExpressionList; + +class VariableDeclaration : public Statement +{ + TypeQualifiers qual_; + ScopedRoot name_; + Expression *initialization_; + ExpressionList *dims_; + Expression *type_; + VariableSymbol *sym_; + + public: + VariableDeclaration(const SourcePosition &pos, TypeQualifiers qual, Handle name, + Expression *type, ExpressionList *dims, Expression *initialization) + : Statement(pos), + qual_(qual), + name_(name), + initialization_(initialization), + dims_(dims), + type_(type), + sym_(NULL) + { + } + + DECLARE_NODE(VariableDeclaration); + + Expression *initialization() const { + return initialization_; + } + Handle name() { + return name_; + } + Expression *declType() const { + return type_; + } + void setSymbol(VariableSymbol *sym) { + assert(!sym_); + sym_ = sym; + } + VariableSymbol *sym() const { + return sym_; + } + Expression *type() const { + return type_; + } + ExpressionList *dims() const { + return dims_; + } + TypeQualifiers quals() const { + return qual_; + } +}; + +class Parameter : public PoolObject +{ + ScopedRoot name_; + Expression *type_; + bool reference_; + Expression *initializer_; + VariableSymbol *sym_; + SourcePosition pos_; + ExpressionList *dims_; + TypeQualifiers quals_; + + public: + Parameter(Handle name, Expression *type, bool reference, TypeQualifiers quals, + ExpressionList *dims, Expression *initializer, const SourcePosition &pos) + : name_(name), + type_(type), + reference_(reference), + initializer_(initializer), + sym_(NULL), + pos_(pos), + dims_(dims), + quals_(quals) + { + } + + Expression *type() const { + return type_; + } + Handle name() const { + return name_; + } + bool reference() const { + return reference_; + } + TypeQualifiers quals() const { + return quals_; + } + ExpressionList *dims() const { + return dims_; + } + Expression *initializer() const { + return initializer_; + } + void setSymbol(VariableSymbol *sym) { + assert(!sym_); + sym_ = sym; + } + VariableSymbol *sym() const { + return sym_; + } + const SourcePosition &pos() const { + return pos_; + } +}; + +class NameProxy : public Expression +{ + ScopedRoot name_; + Symbol *binding_; + + public: + NameProxy(const SourcePosition &pos, Handle name) + : Expression(pos), + name_(name), + binding_(NULL) + { + } + + DECLARE_NODE(NameProxy); + + Handle name() const { + return name_; + } + Symbol *sym() const { + return binding_; + } + void bind(Symbol *sym) { + binding_ = sym; + } +}; + +class BooleanLiteral : public Expression +{ + TokenKind token_; + + public: + BooleanLiteral(const SourcePosition &pos, TokenKind token) + : Expression(pos), + token_(token) + { + } + + DECLARE_NODE(BooleanLiteral); + TokenKind token() const { + return token_; + } +}; + +class IntegerLiteral : public Expression +{ + int value_; + + public: + IntegerLiteral(const SourcePosition &pos, int value) + : Expression(pos), + value_(value) + { + } + + DECLARE_NODE(IntegerLiteral); + + int value() const { + return value_; + } +}; + +class FloatLiteral : public Expression +{ + double value_; + + public: + FloatLiteral(const SourcePosition &pos, double value) + : Expression(pos), + value_(value) + { + } + + DECLARE_NODE(FloatLiteral); + + double value() const { + return value_; + } +}; + +class StringLiteral : public Expression +{ + ScopedRoot string_; + + public: + StringLiteral(const SourcePosition &pos, String *string) + : Expression(pos), + string_(string) + { + } + + DECLARE_NODE(StringLiteral); + + String *string() const { + return string_; + } +}; + +class ArrayLiteral : public Expression +{ + TokenKind token_; + ExpressionList *expressions_; + + public: + ArrayLiteral(const SourcePosition &pos, TokenKind token, ExpressionList *expressions) + : Expression(pos), + token_(token), + expressions_(expressions) + { + } + + DECLARE_NODE(ArrayLiteral); + + TokenKind token() const { + return token_; + } + ExpressionList *expressions() const { + return expressions_; + } + bool isFixed() const { + return token_ == TOK_LBRACE; + } +}; + +class UnaryExpression : public Expression +{ + Expression *expression_; + TokenKind token_; + NameProxy *tag_; + + public: + UnaryExpression(const SourcePosition &pos, TokenKind token, Expression *expr) + : Expression(pos), + expression_(expr), + token_(token), + tag_(NULL) + { + } + + UnaryExpression(const SourcePosition &pos, TokenKind token, Expression *expr, + NameProxy *tag) + : Expression(pos), + expression_(expr), + token_(token), + tag_(tag) + { + } + + DECLARE_NODE(UnaryExpression); + + Expression *expression() const { + return expression_; + } + TokenKind token() const { + return token_; + } + NameProxy *tag() const { + return tag_; + } +}; + +class BinaryExpression : public Expression +{ + Expression *left_; + Expression *right_; + TokenKind token_; + + public: + BinaryExpression(const SourcePosition &pos, TokenKind token, Expression *left, Expression *right) + : Expression(pos), + left_(left), + right_(right), + token_(token) + { + } + + DECLARE_NODE(BinaryExpression); + + Expression *left() const { + return left_; + } + Expression *right() const { + return right_; + } + TokenKind token() const { + return token_; + } +}; + +class TernaryExpression : public Expression +{ + Expression *condition_; + Expression *left_; + Expression *right_; + + public: + TernaryExpression(const SourcePosition &pos, Expression *condition, Expression *left, Expression *right) + : Expression(pos), + condition_(condition), + left_(left), + right_(right) + { + } + + DECLARE_NODE(TernaryExpression); + + Expression *condition() const { + return condition_; + } + Expression *left() const { + return left_; + } + Expression *right() const { + return right_; + } +}; + +class IndexExpression : public Expression +{ + Expression *left_; + Expression *right_; + + public: + IndexExpression(const SourcePosition &pos, Expression *left, Expression *right) + : Expression(pos), + left_(left), + right_(right) + { + } + + DECLARE_NODE(IndexExpression); + + Expression *left() const { + return left_; + } + Expression *right() const { + return right_; + } +}; + +class FieldExpression : public Expression +{ + Expression *left_; + ScopedRoot field_; + + public: + FieldExpression(const SourcePosition &pos, Expression *left, Handle field) + : Expression(pos), + left_(left), + field_(field) + { + } + + DECLARE_NODE(FieldExpression); + + Expression *left() const { + return left_; + } + Handle field() const { + return field_; + } +}; + +class CallExpression : public Expression +{ + Expression *callee_; + ExpressionList *arguments_; + + public: + CallExpression(const SourcePosition &pos, Expression *callee, ExpressionList *arguments) + : Expression(pos), + callee_(callee), + arguments_(arguments) + { + } + + DECLARE_NODE(CallExpression); + + Expression *callee() const { + return callee_; + } + ExpressionList *arguments() const { + return arguments_; + } +}; + +class ForStatement : public Statement +{ + Statement *initialization_; + Expression *condition_; + Statement *update_; + Statement *body_; + Scope *scope_; + + public: + ForStatement(const SourcePosition &pos, Statement *initialization, + Expression *condition, Statement *update, Statement *body) + : Statement(pos), + initialization_(initialization), + condition_(condition), + update_(update), + body_(body), + scope_(NULL) + { + } + + DECLARE_NODE(ForStatement); + + Statement *initialization() const { + return initialization_; + } + Expression *condition() const { + return condition_; + } + Statement *update() const { + return update_; + } + Statement *body() const { + return body_; + } + Scope *scope() const { + return scope_; + } + void setScope(Scope *scope) { + scope_ = scope; + } +}; + +class WhileStatement : public Statement +{ + TokenKind token_; + Expression *condition_; + Statement *body_; + + public: + WhileStatement(const SourcePosition &pos, TokenKind kind, Expression *condition, Statement *body) + : Statement(pos), + token_(kind), + condition_(condition), + body_(body) + { + } + + DECLARE_NODE(WhileStatement); + + TokenKind token() const { + return token_; + } + Expression *condition() const { + return condition_; + } + Statement *body() const { + return body_; + } +}; + +class ReturnStatement : public Statement +{ + Expression *expression_; + + public: + ReturnStatement(const SourcePosition &pos, Expression *expression) + : Statement(pos), + expression_(expression) + { + } + + DECLARE_NODE(ReturnStatement); + + Expression *expression() const { + return expression_; + } +}; + +class BreakStatement : public Statement +{ + public: + BreakStatement(const SourcePosition &pos) + : Statement(pos) + { + } + + DECLARE_NODE(BreakStatement); +}; + +class ContinueStatement : public Statement +{ + public: + ContinueStatement(const SourcePosition &pos) + : Statement(pos) + { + } + + DECLARE_NODE(ContinueStatement); +}; + +class Assignment : public Expression +{ + TokenKind token_; + Expression *lvalue_; + Expression *expression_; + + public: + Assignment(const SourcePosition &pos, TokenKind token, Expression *left, Expression *right) + : Expression(pos), + token_(token), + lvalue_(left), + expression_(right) + { + } + + DECLARE_NODE(Assignment); + + TokenKind token() const { + return token_; + } + Expression *lvalue() const { + return lvalue_; + } + Expression *expression() const { + return expression_; + } +}; + +class ExpressionStatement : public Statement +{ + Expression *expression_; + + public: + ExpressionStatement(Expression *expression) + : Statement(expression->pos()), + expression_(expression) + { + } + + DECLARE_NODE(ExpressionStatement); + + Expression *expression() const { + return expression_; + } +}; + +class BlockStatement : public Statement +{ + StatementList *statements_; + Scope *scope_; + TokenKind type_; + + public: + BlockStatement(const SourcePosition &pos, StatementList *statements, TokenKind kind) + : Statement(pos), + statements_(statements), + scope_(NULL), + type_(kind) + { + } + + DECLARE_NODE(BlockStatement); + + StatementList *statements() const { + return statements_; + } + Scope *scope() const { + return scope_; + } + void setScope(Scope *scope) { + scope_ = scope; + } + TokenKind type() const { + return type_; + } +}; + +typedef PoolList ParameterList; + +class FunctionSignature +{ + public: + FunctionSignature(Expression *returnType, ParameterList *parameters, bool variadic) + : returnType_(returnType), + parameters_(parameters), + variadic_(variadic) + { + } + + Expression *returnType() const { + return returnType_; + } + ParameterList *parameters() const { + return parameters_; + } + bool variadic() const { + return variadic_; + } + + private: + Expression *returnType_; + ParameterList *parameters_; + bool variadic_; +}; + +class FunctionTypeStatement : public Statement +{ + public: + FunctionTypeStatement(const SourcePosition &pos, Handle name, const FunctionSignature &signature) + : Statement(pos), + name_(name), + signature_(signature) + { + } + + DECLARE_NODE(FunctionTypeStatement); + + Handle name() const { + return name_; + } + const FunctionSignature &signature() const { + return signature_; + } + + private: + ScopedRoot name_; + FunctionSignature signature_; + +}; + +class FunctionStatement : public Statement +{ + ScopedRoot name_; + TokenKind kind_; + Statement *body_; + FunctionSignature signature_; + bool explicitReturn_; + FunctionSymbol *sym_; + Scope *argScope_; + Scope *varScope_; + + public: + FunctionStatement(const SourcePosition &pos, Handle name, TokenKind kind, + Statement *body, const FunctionSignature &signature, bool explicitReturn) + : Statement(pos), + name_(name), + kind_(kind), + body_(body), + signature_(signature), + explicitReturn_(explicitReturn), + sym_(NULL), + argScope_(NULL), + varScope_(NULL) + { + } + + DECLARE_NODE(FunctionStatement); + + Handle name() const { + return name_; + } + Statement *body() const { + return body_; + } + const FunctionSignature &signature() { + return signature_; + } + bool hasExplicitReturn() const { + return explicitReturn_; + } + TokenKind token() const { + return kind_; + } + void setSymbol(FunctionSymbol *sym) { + assert(!sym_); + sym_ = sym; + } + FunctionSymbol *sym() const { + return sym_; + } + void setScopes(Scope *argScope, Scope *varScope) { + argScope_ = argScope; + varScope_ = varScope; + } + Scope *argScope() const { + return argScope_; + } + Scope *varScope() const { + return varScope_; + } +}; + +class IfStatement : public Statement +{ + Expression *condition_; + Statement *ifTrue_; + Statement *ifFalse_; + + public: + IfStatement(const SourcePosition &pos, Expression *condition, Statement *ifTrue) + : Statement(pos), + condition_(condition), + ifTrue_(ifTrue), + ifFalse_(NULL) + { + } + + DECLARE_NODE(IfStatement); + + Expression *condition() const { + return condition_; + } + Statement *ifTrue() const { + return ifTrue_; + } + Statement *ifFalse() const { + return ifFalse_; + } + void setIfFalse(Statement *ifFalse) { + ifFalse_ = ifFalse; + } +}; + +class EnumStatement : public Statement +{ + ScopedRoot name_; + TypeSymbol *sym_; + + public: + struct Entry { + ScopedRoot name; + Expression *expr; + SourcePosition pos; + ConstantSymbol *sym; + + Entry(Handle name, Expression *expr, const SourcePosition &pos) + : name(name), + expr(expr), + pos(pos), + sym(NULL) + { + } + }; + + typedef PoolList EntryList; + + public: + EnumStatement(const SourcePosition &pos, TokenKind visibility, Handle name, EntryList *entries) + : Statement(pos), + name_(name), + sym_(NULL), + entries_(entries), + visibility_(visibility) + { + } + + DECLARE_NODE(EnumStatement); + + Handle name() const { + return name_; + } + EntryList *entries() const { + return entries_; + } + void setSymbol(TypeSymbol *sym) { + assert(!sym_); + sym_ = sym; + } + TypeSymbol *sym() const { + return sym_; + } + TokenKind visibility() const { + return visibility_; + } + + private: + EntryList *entries_; + TokenKind visibility_; +}; + +class IncDecExpression : public Expression +{ + TokenKind token_; + Expression *expression_; + bool postfix_; + + public: + IncDecExpression(const SourcePosition &pos, TokenKind token, Expression *expression, bool postfix) + : Expression(pos), + token_(token), + expression_(expression), + postfix_(postfix) + { + } + + DECLARE_NODE(IncDecExpression); + + TokenKind token() const { + return token_; + } + Expression *expression() const { + return expression_; + } + bool postfix() const { + return postfix_; + } +}; + +class Case : public PoolObject +{ + public: + Case(Expression *expression, ExpressionList *others, Statement *statement) + : expression_(expression), + others_(others), + statement_(statement) + { + } + + Expression *expression() const { + return expression_; + } + PoolList *others() const { + return others_; + } + Statement *statement() const { + return statement_; + } + + private: + Expression *expression_; + PoolList *others_; + Statement *statement_; +}; + +struct CaseValue +{ + BoxedPrimitive box; + size_t statement; + + CaseValue(size_t statement) + : statement(statement) + { + } +}; + +typedef PoolList CaseValueList; + +class SwitchStatement : public Statement +{ + public: + SwitchStatement(const SourcePosition &pos, Expression *expression, PoolList *cases, + Statement *def) + : Statement(pos), + expression_(expression), + cases_(cases), + default_(def), + table_(NULL) + { + } + + DECLARE_NODE(SwitchStatement); + + Expression *expression() const { + return expression_; + } + PoolList *cases() const { + return cases_; + } + Statement *defaultCase() const { + return default_; + } + void setCaseValueList(CaseValueList *list) { + table_ = list; + } + CaseValueList *caseValueList() const { + return table_; + } + + private: + Expression *expression_; + PoolList *cases_; + Statement *default_; + CaseValueList *table_; +}; + +class StructureStatement : public Statement +{ + public: + class Field : public PoolObject + { + SourcePosition pos_; + Expression *type_; + ScopedRoot name_; + + public: + Field(const SourcePosition &pos, + Expression *type, + Handle name) + : pos_(pos), + type_(type), + name_(name) + { + } + + const SourcePosition &pos() const { + return pos_; + } + Handle name() const { + return name_; + } + Expression *type() const { + return type_; + } + }; + + typedef PoolList FieldList; + + public: + StructureStatement(const SourcePosition &pos, Handle name, FieldList *fields) + : Statement(pos), + name_(name), + fields_(fields), + sym_(NULL) + { + } + + DECLARE_NODE(StructureStatement); + + FieldList *fields() const { + return fields_; + } + Handle name() const { + return name_; + } + void setSymbol(TypeSymbol *sym) { + assert(!sym_); + sym_ = sym; + } + TypeSymbol *sym() const { + return sym_; + } + + private: + ScopedRoot name_; + FieldList *fields_; + TypeSymbol *sym_; +}; + +typedef PoolList *> NameList; + +class ImportStatement : public Statement +{ + public: + ImportStatement(const SourcePosition &pos, PathComponent *path, NameList *names) + : Statement(pos), + path_(path), + names_(names) + { + } + + DECLARE_NODE(ImportStatement); + + PathComponent *path() const { + return path_; + } + void setSource(Importable *source) { + source_ = source; + } + Handle source() { + return source_; + } + unsigned importIndex() const { + return importIndex_; + } + void setImportIndex(unsigned index) { + importIndex_ = index; + } + NameList *names() const { + return names_; + } + + bool importAllNames() const { + return names_ && names_->empty(); + } + + private: + PathComponent *path_; + ScopedRoot source_; + unsigned importIndex_; + NameList *names_; +}; + +typedef StructureStatement::FieldList FieldList; +typedef PoolList NameProxyList; + +#undef DECLARE_NODE + +class ParseTree : public PoolObject +{ + StatementList *statements_; + NameProxyList *unbound_; + PoolList *imports_; + + public: + ParseTree(StatementList *statements, NameProxyList *unbound, PoolList *imports) + : statements_(statements), + unbound_(unbound), + imports_(imports) + { + } + + void dump(FILE *fp); + + StatementList *statements() const { + return statements_; + } + NameProxyList *unbound() const { + return unbound_; + } + PoolList *imports() const { + return imports_; + } +}; + +} + +#endif // _include_sourcepawn_ast_h_ diff -r c4b1341297e5 -r ba85a47ee414 src/compiler/BytecodeEmitter.cpp --- a/src/compiler/BytecodeEmitter.cpp Mon Jun 10 00:46:29 2013 +0100 +++ b/src/compiler/BytecodeEmitter.cpp Mon Jun 10 01:04:43 2013 +0100 @@ -1,386 +1,386 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of JITCraft. - * - * JITCraft is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * JITCraft. If not, see http://www.gnu.org/licenses/. - */ -#include -#include -#include "../Utility.h" -#include "../Types.h" -#include "../Zone.h" -#include "../Strings.h" -#include "../Zone.h" -#include "BytecodeEmitter.h" -#include "Symbols.h" -#include "Scopes.h" -#include "../Spaces-inl.h" -#include "../Heap-inl.h" - -using namespace ke; - -StringTable::StringTable() - : set_(ZoneAllocatorPolicy()), - initialized_(false) -{ -} - -bool -StringTable::initialize() -{ - assert(!initialized_); - initialized_ = true; - if (!set_.init(16)) - return false; - return true; -} - -bool -StringTable::add(Handle name, unsigned *indexp) -{ - if (!initialized_ && !initialize()) - return false; - - Set::Insert p = set_.findForAdd(*name); - if (p.found()) { - *indexp = p->index; - return true; - } - - if (strings_.length() > INT_MAX) { - ZONE()->reportAllocationOverflow(); - return false; - } - - Entry e(name, strings_.length()); - if (!strings_.append(e)) - return false; - - if (!set_.add(p, e)) - return false; - - *indexp = e.index; - - return true; -} - -void -StringTable::accept(VirtualObjectVisitor *visitor) -{ - for (size_t i = 0; i < strings_.length(); i++) - visitor->visit(reinterpret_cast(&strings_[i].string)); -} - -BytecodeEmitter::BytecodeEmitter() - : AutoRooted(ZONE()), - buffer_(inline_buffer_), - buffer_pos_(buffer_), - buffer_end_(buffer_pos_ + ARRAY_LENGTH(inline_buffer_)), - oom_(false), - maxStackDepth_(0), - loopDepth_(0), - maxLoopDepth_(0) -{ - lastCodePosition_ = 0; - firstLine_ = 1; -} - -BytecodeEmitter::~BytecodeEmitter() -{ - for (size_t i = 0; i < oopRecords_.length(); i++) - delete oopRecords_[i]; -} - -void -BytecodeEmitter::accept(VirtualObjectVisitor *visitor) -{ - for (size_t i = 0; i < objects_.length(); i++) - visitor->visit(&objects_[i]); - - strings_.accept(visitor); -} - -bool -BytecodeEmitter::growBuffer(size_t nbytes) -{ - if (oom_) { - buffer_pos_ = buffer_; - return false; - } - - size_t newSize = size_t((buffer_end_ - buffer_) * 2); - if (newSize >= MAX_LENGTH) { - oom_ = true; - buffer_pos_ = buffer_; - ZONE()->reportOutOfMemory(); - return false; - } - - bytecode *newBuffer; - if (buffer_ == inline_buffer_) - newBuffer = (bytecode *)malloc(newSize); - else - newBuffer = (bytecode *)realloc(buffer_, newSize); - - // If we can't allocate a buffer, reset the buffer to the start and set - // a flag to indicate out-of-memory. - if (!newBuffer) { - oom_ = true; - buffer_pos_ = buffer_; - ZONE()->reportOutOfMemory(); - return false; - } - - if (buffer_ == inline_buffer_) - memcpy(newBuffer, inline_buffer_, (buffer_pos_ - inline_buffer_)); - - buffer_pos_ = newBuffer + size_t(buffer_pos_ - buffer_); - buffer_ = newBuffer; - buffer_end_ = buffer_ + newSize; - assert(buffer_pos_ + nbytes < buffer_end_); - - return true; -} - -void -BytecodeEmitter::recordOops() -{ - OopRecord *record = new OopRecord(oopMap_.length(), position()); - - if (!record->slots.ensure(record->depth)) { - oom_ = true; - return; - } - - bool hadOops = false; - for (size_t i = 0; i < oopMap_.length(); i++) { - bool bit = oopMap_[i]; - record->slots.infallibleAppend(bit); - hadOops |= bit; - } - - if (!hadOops) { - delete record; - return; - } - - oom_ &= oopRecords_.append(record); -} - -void -BytecodeEmitter::note_position(const SourcePosition &pos) -{ - assert(pos.line > 0); - assert(unsigned(pos.line) >= firstLine_); - - unsigned lineDelta = unsigned(pos.line) - firstLine_; - unsigned pcDelta = unsigned(position()) - lastCodePosition_; - - sourcemap_.writeUint32(pcDelta); - sourcemap_.writeUint32(lineDelta); - sourcemap_.writeUint32(pos.col); - - lastCodePosition_ = unsigned(position()); -} - -void -BytecodeEmitter::getglobal(Symbol *sym) -{ - ensure(OP_GETGLOBAL_LENGTH); - writeOp(OP_GETGLOBAL); - writeUint32(addString(sym->name())); - if (sym->type()->isTraceable()) - markOop(1); -} -void -BytecodeEmitter::setglobal(Symbol *sym) -{ - ensure(OP_SETGLOBAL_LENGTH); - writeOp(OP_SETGLOBAL); - writeUint32(addString(sym->name())); - if (sym->type()->isTraceable()) - markOop(1); -} - -void -BytecodeEmitter::getlocal(VariableSymbol *var) -{ - assert(var->storage() == VariableSymbol::Local); - ensure(OP_GETLOCAL_LENGTH); - writeOp(OP_GETLOCAL); - writeUint32(var->slot()); - if (var->type()->isTraceable()) - markOop(1); -} - -void -BytecodeEmitter::setlocal(VariableSymbol *var) -{ - assert(var->storage() == VariableSymbol::Local); - ensure(OP_SETLOCAL_LENGTH); - writeOp(OP_SETLOCAL); - writeUint32(var->slot()); - if (var->type()->isTraceable()) - markOop(1); -} - -void -BytecodeEmitter::getarg(VariableSymbol *var) -{ - assert(var->storage() == VariableSymbol::Arg); - ensure(OP_GETARG_LENGTH); - writeOp(OP_GETARG); - writeUint32(var->slot()); - if (var->type()->isTraceable()) - markOop(1); -} - -void -BytecodeEmitter::setarg(VariableSymbol *var) -{ - assert(var->storage() == VariableSymbol::Arg); - ensure(OP_SETARG_LENGTH); - writeOp(OP_SETARG); - writeUint32(var->slot()); - if (var->type()->isTraceable()) - markOop(1); -} - -bool -BytecodeEmitter::allocate(VariableSymbol *sym) -{ - assert(sym->isVariable()); - assert(sym->scope()->kind() == Scope::BLOCK); - - switch (sym->scope()->kind()) { - case Scope::BLOCK: - { - // See overall comment on overflows. - assert(locals_.length() < UINT_MAX); - sym->allocate(VariableSymbol::Local, locals_.length()); - if (!locals_.append(sym)) - return false; - break; - } - - default: - assert(false); - return false; - } - - return true; -} - -void -BytecodeEmitter::newref(VariableSymbol *var) -{ - recordOops(); - if (var->storage() == VariableSymbol::Local) { - ensure(OP_VARREF_LIFO_LENGTH); - writeOp(OP_VARREF_LIFO); - } else { - assert(var->storage() == VariableSymbol::Arg); - ensure(OP_ARGREF_LIFO_LENGTH); - writeOp(OP_ARGREF_LIFO); - } - writeUint32(var->slot()); - markOop(1); -} - -Code * -BytecodeEmitter::finish() -{ - stop(); - - Zone *zone = ZONE(); - if (oom() || zone->hasPendingException()) - return NULL; - - Local bytes(zone, ByteArray::New(zone, length(), Heap::Tenure_Old)); - if (!bytes) - return NULL; - memcpy(bytes->raw(), buffer(), length()); - - Local objects(zone, FixedArray::New(zone, objects_.length(), Heap::Tenure_Old)); - if (!objects) - return NULL; - - for (size_t i = 0; i < objects_.length(); i++) - objects->set(zone, i, objects_[i]); - - // If any stack slots hold gc pointers at potential failure points in the - // bytecode, record them now. We do this by building a list of tuples; - // each tuple contains the pc offset, and a bitset containing which stack - // slots must be scanned by the gc. - Local safepoints(zone); - if (oopRecords_.length()) { - size_t bytesPerSafepoint = Code::BytesPerSafepoint(maxStackDepth()); - size_t gcmapSize = bytesPerSafepoint * oopRecords_.length(); - safepoints = ByteArray::New(zone, gcmapSize, Heap::Tenure_Old); - if (!safepoints) - return NULL; - - Address cursor = safepoints->raw(); - for (size_t i = 0; i < oopRecords_.length(); i++) { - OopRecord *record = oopRecords_[i]; - assert(record->depth <= maxStackDepth()); - - *reinterpret_cast(cursor) = record->offset; - - unsigned char *bits = reinterpret_cast(cursor + sizeof(unsigned)); - for (unsigned i = 0; i < record->depth; i++) - bits[i / 8] |= (1 << (i % 8)); - - cursor += bytesPerSafepoint; - } - - assert(cursor == safepoints->raw() + gcmapSize); - } - - Local sourcemap(zone); - if (sourcemap_.length()) { - sourcemap = ByteArray::New(zone, sourcemap_.length(), Heap::Tenure_Old); - if (!sourcemap) - return NULL; - - memcpy(sourcemap->raw(), sourcemap_.buffer(), sourcemap_.length()); - } - - Local strings(zone, FixedArray::New(zone, strings_.stringCount(), Heap::Tenure_Old)); - if (!strings) - return NULL; - for (size_t i = 0; i < strings_.stringCount(); i++) - strings->set(zone, i, strings_.stringAt(i)); - - Local locals(zone, FixedArray::New(zone, locals_.length(), Heap::Tenure_Old)); - if (!locals) - return NULL; - for (size_t i = 0; i < locals_.length(); i++) - locals->set(zone, i, locals_[i]->type()); - - Local code(zone); - - code = Code::New(zone, - bytes, - locals, - objects, - strings, - maxStackDepth(), - maxLoopDepth_, - safepoints, - sourcemap); - - return code; -} +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include +#include +#include "../Utility.h" +#include "../Types.h" +#include "../Zone.h" +#include "../Strings.h" +#include "../Zone.h" +#include "BytecodeEmitter.h" +#include "Symbols.h" +#include "Scopes.h" +#include "../Spaces-inl.h" +#include "../Heap-inl.h" + +using namespace ke; + +StringTable::StringTable() + : set_(ZoneAllocatorPolicy()), + initialized_(false) +{ +} + +bool +StringTable::initialize() +{ + assert(!initialized_); + initialized_ = true; + if (!set_.init(16)) + return false; + return true; +} + +bool +StringTable::add(Handle name, unsigned *indexp) +{ + if (!initialized_ && !initialize()) + return false; + + Set::Insert p = set_.findForAdd(*name); + if (p.found()) { + *indexp = p->index; + return true; + } + + if (strings_.length() > INT_MAX) { + ZONE()->reportAllocationOverflow(); + return false; + } + + Entry e(name, strings_.length()); + if (!strings_.append(e)) + return false; + + if (!set_.add(p, e)) + return false; + + *indexp = e.index; + + return true; +} + +void +StringTable::accept(VirtualObjectVisitor *visitor) +{ + for (size_t i = 0; i < strings_.length(); i++) + visitor->visit(reinterpret_cast(&strings_[i].string)); +} + +BytecodeEmitter::BytecodeEmitter() + : AutoRooted(ZONE()), + buffer_(inline_buffer_), + buffer_pos_(buffer_), + buffer_end_(buffer_pos_ + ARRAY_LENGTH(inline_buffer_)), + oom_(false), + maxStackDepth_(0), + loopDepth_(0), + maxLoopDepth_(0) +{ + lastCodePosition_ = 0; + firstLine_ = 1; +} + +BytecodeEmitter::~BytecodeEmitter() +{ + for (size_t i = 0; i < oopRecords_.length(); i++) + delete oopRecords_[i]; +} + +void +BytecodeEmitter::accept(VirtualObjectVisitor *visitor) +{ + for (size_t i = 0; i < objects_.length(); i++) + visitor->visit(&objects_[i]); + + strings_.accept(visitor); +} + +bool +BytecodeEmitter::growBuffer(size_t nbytes) +{ + if (oom_) { + buffer_pos_ = buffer_; + return false; + } + + size_t newSize = size_t((buffer_end_ - buffer_) * 2); + if (newSize >= MAX_LENGTH) { + oom_ = true; + buffer_pos_ = buffer_; + ZONE()->reportOutOfMemory(); + return false; + } + + bytecode *newBuffer; + if (buffer_ == inline_buffer_) + newBuffer = (bytecode *)malloc(newSize); + else + newBuffer = (bytecode *)realloc(buffer_, newSize); + + // If we can't allocate a buffer, reset the buffer to the start and set + // a flag to indicate out-of-memory. + if (!newBuffer) { + oom_ = true; + buffer_pos_ = buffer_; + ZONE()->reportOutOfMemory(); + return false; + } + + if (buffer_ == inline_buffer_) + memcpy(newBuffer, inline_buffer_, (buffer_pos_ - inline_buffer_)); + + buffer_pos_ = newBuffer + size_t(buffer_pos_ - buffer_); + buffer_ = newBuffer; + buffer_end_ = buffer_ + newSize; + assert(buffer_pos_ + nbytes < buffer_end_); + + return true; +} + +void +BytecodeEmitter::recordOops() +{ + OopRecord *record = new OopRecord(oopMap_.length(), position()); + + if (!record->slots.ensure(record->depth)) { + oom_ = true; + return; + } + + bool hadOops = false; + for (size_t i = 0; i < oopMap_.length(); i++) { + bool bit = oopMap_[i]; + record->slots.infallibleAppend(bit); + hadOops |= bit; + } + + if (!hadOops) { + delete record; + return; + } + + oom_ &= oopRecords_.append(record); +} + +void +BytecodeEmitter::note_position(const SourcePosition &pos) +{ + assert(pos.line > 0); + assert(unsigned(pos.line) >= firstLine_); + + unsigned lineDelta = unsigned(pos.line) - firstLine_; + unsigned pcDelta = unsigned(position()) - lastCodePosition_; + + sourcemap_.writeUint32(pcDelta); + sourcemap_.writeUint32(lineDelta); + sourcemap_.writeUint32(pos.col); + + lastCodePosition_ = unsigned(position()); +} + +void +BytecodeEmitter::getglobal(Symbol *sym) +{ + ensure(OP_GETGLOBAL_LENGTH); + writeOp(OP_GETGLOBAL); + writeUint32(addString(sym->name())); + if (sym->type()->isTraceable()) + markOop(1); +} +void +BytecodeEmitter::setglobal(Symbol *sym) +{ + ensure(OP_SETGLOBAL_LENGTH); + writeOp(OP_SETGLOBAL); + writeUint32(addString(sym->name())); + if (sym->type()->isTraceable()) + markOop(1); +} + +void +BytecodeEmitter::getlocal(VariableSymbol *var) +{ + assert(var->storage() == VariableSymbol::Local); + ensure(OP_GETLOCAL_LENGTH); + writeOp(OP_GETLOCAL); + writeUint32(var->slot()); + if (var->type()->isTraceable()) + markOop(1); +} + +void +BytecodeEmitter::setlocal(VariableSymbol *var) +{ + assert(var->storage() == VariableSymbol::Local); + ensure(OP_SETLOCAL_LENGTH); + writeOp(OP_SETLOCAL); + writeUint32(var->slot()); + if (var->type()->isTraceable()) + markOop(1); +} + +void +BytecodeEmitter::getarg(VariableSymbol *var) +{ + assert(var->storage() == VariableSymbol::Arg); + ensure(OP_GETARG_LENGTH); + writeOp(OP_GETARG); + writeUint32(var->slot()); + if (var->type()->isTraceable()) + markOop(1); +} + +void +BytecodeEmitter::setarg(VariableSymbol *var) +{ + assert(var->storage() == VariableSymbol::Arg); + ensure(OP_SETARG_LENGTH); + writeOp(OP_SETARG); + writeUint32(var->slot()); + if (var->type()->isTraceable()) + markOop(1); +} + +bool +BytecodeEmitter::allocate(VariableSymbol *sym) +{ + assert(sym->isVariable()); + assert(sym->scope()->kind() == Scope::BLOCK); + + switch (sym->scope()->kind()) { + case Scope::BLOCK: + { + // See overall comment on overflows. + assert(locals_.length() < UINT_MAX); + sym->allocate(VariableSymbol::Local, locals_.length()); + if (!locals_.append(sym)) + return false; + break; + } + + default: + assert(false); + return false; + } + + return true; +} + +void +BytecodeEmitter::newref(VariableSymbol *var) +{ + recordOops(); + if (var->storage() == VariableSymbol::Local) { + ensure(OP_VARREF_LIFO_LENGTH); + writeOp(OP_VARREF_LIFO); + } else { + assert(var->storage() == VariableSymbol::Arg); + ensure(OP_ARGREF_LIFO_LENGTH); + writeOp(OP_ARGREF_LIFO); + } + writeUint32(var->slot()); + markOop(1); +} + +Code * +BytecodeEmitter::finish() +{ + stop(); + + Zone *zone = ZONE(); + if (oom() || zone->hasPendingException()) + return NULL; + + Local bytes(zone, ByteArray::New(zone, length(), Heap::Tenure_Old)); + if (!bytes) + return NULL; + memcpy(bytes->raw(), buffer(), length()); + + Local objects(zone, FixedArray::New(zone, objects_.length(), Heap::Tenure_Old)); + if (!objects) + return NULL; + + for (size_t i = 0; i < objects_.length(); i++) + objects->set(zone, i, objects_[i]); + + // If any stack slots hold gc pointers at potential failure points in the + // bytecode, record them now. We do this by building a list of tuples; + // each tuple contains the pc offset, and a bitset containing which stack + // slots must be scanned by the gc. + Local safepoints(zone); + if (oopRecords_.length()) { + size_t bytesPerSafepoint = Code::BytesPerSafepoint(maxStackDepth()); + size_t gcmapSize = bytesPerSafepoint * oopRecords_.length(); + safepoints = ByteArray::New(zone, gcmapSize, Heap::Tenure_Old); + if (!safepoints) + return NULL; + + Address cursor = safepoints->raw(); + for (size_t i = 0; i < oopRecords_.length(); i++) { + OopRecord *record = oopRecords_[i]; + assert(record->depth <= maxStackDepth()); + + *reinterpret_cast(cursor) = record->offset; + + unsigned char *bits = reinterpret_cast(cursor + sizeof(unsigned)); + for (unsigned i = 0; i < record->depth; i++) + bits[i / 8] |= (1 << (i % 8)); + + cursor += bytesPerSafepoint; + } + + assert(cursor == safepoints->raw() + gcmapSize); + } + + Local sourcemap(zone); + if (sourcemap_.length()) { + sourcemap = ByteArray::New(zone, sourcemap_.length(), Heap::Tenure_Old); + if (!sourcemap) + return NULL; + + memcpy(sourcemap->raw(), sourcemap_.buffer(), sourcemap_.length()); + } + + Local strings(zone, FixedArray::New(zone, strings_.stringCount(), Heap::Tenure_Old)); + if (!strings) + return NULL; + for (size_t i = 0; i < strings_.stringCount(); i++) + strings->set(zone, i, strings_.stringAt(i)); + + Local locals(zone, FixedArray::New(zone, locals_.length(), Heap::Tenure_Old)); + if (!locals) + return NULL; + for (size_t i = 0; i < locals_.length(); i++) + locals->set(zone, i, locals_[i]->type()); + + Local code(zone); + + code = Code::New(zone, + bytes, + locals, + objects, + strings, + maxStackDepth(), + maxLoopDepth_, + safepoints, + sourcemap); + + return code; +} diff -r c4b1341297e5 -r ba85a47ee414 src/compiler/BytecodeEmitter.h --- a/src/compiler/BytecodeEmitter.h Mon Jun 10 00:46:29 2013 +0100 +++ b/src/compiler/BytecodeEmitter.h Mon Jun 10 01:04:43 2013 +0100 @@ -1,717 +1,717 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of JITCraft. - * - * JITCraft is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * JITCraft. If not, see http://www.gnu.org/licenses/. - */ -#ifndef _include_bytecode_emitter_h_ -#define _include_bytecode_emitter_h_ - -#include -#include -#include "../Opcodes.h" -#include "../Handles.h" -#include "../CompactBuffer.h" -#include "../Array.h" -#include "../HashTable.h" -#include "../VirtualObjectVisitor.h" - -namespace ke { - -// An overarching note on overflows: Though we should use CheckedInt, we rely -// on a source-length cap covering all possible overflow checks. As long as -// the source length is capped to INT_MAX, the number of AST nodes will be -// strictly beneath that cap, and so to will most things generated by the -// AST. For example, local and argument counts, number of functions, fields -// in a struct, et cetera. Not everything can be covered by this cap: the -// stack depth, number of bytecodes for example. In these cases we explicitly -// test for overflow of INT_MAX and in other cases we assert. -// -// In the future, we should of course use a checked int wrapper. - -class Type; -class Map; -class ArrayMap; -class Symbol; -class VariableSymbol; - -struct OopRecord -{ - unsigned depth; - unsigned offset; - Vector slots; - - OopRecord(unsigned length, unsigned offset) - : depth(length), - offset(offset) - { - } -}; - -class StringTable -{ - struct Entry - { - String *string; - unsigned index; - - Entry() - : string(NULL), - index(0) - { - } - - Entry(String *string, unsigned index) - : string(string), - index(index) - { - } - }; - - struct Policy - { - typedef Entry Payload; - - static uint32 hash(String *string) { - return HashPointer(string); - } - - static bool matches(String *key, Entry &e) { - return key == e.string; - } - }; - - typedef HashTable Set; - - public: - StringTable(); - bool initialize(); - void accept(VirtualObjectVisitor *visitor); - - bool add(Handle name, unsigned *indexp); - size_t stringCount() const { - return strings_.length(); - } - String *stringAt(size_t i) const { - return strings_[i].string; - } - - private: - Set set_; - Vector strings_; - bool initialized_; -}; - -// The bytecode emitter assists in building Code objects, by emitting opcodes -// and tracking stack information. Since the SemA creates temporary pool/root -// scopes, we cannot use ScopedRoot or PoolObjects. Instead, BCE gets a -// special marking function. -class BytecodeEmitter : public AutoRooted -{ - private: - bytecode *buffer_; - bytecode *buffer_pos_; - bytecode *buffer_end_; - bytecode inline_buffer_[512]; - bool oom_; - unsigned maxStackDepth_; - Vector oopMap_; - Vector oopRecords_; - CompactBufferWriter sourcemap_; - unsigned lastCodePosition_; - unsigned firstLine_; - StringTable strings_; - Vector locals_; - Vector objects_; - unsigned loopDepth_; - unsigned maxLoopDepth_; - - private: - // The maximum size of a code object must be such that the distance between - // two instructions fits into a signed, 31-bit integer. This ensures that - // all code-bounded buffers (like the number of object literals or - // safepoints) does not need to be bounds checked. It also means we can add - // any two offsets and be guaranteed that they don't overflow. - static const size_t MAX_LENGTH = INT_MAX / 2; - - private: - bool growBuffer(size_t bytes); - - // Checking the return value is not necessary, since the inline buffer is - // used as a ballast. - bool ensure(size_t bytes) { - if (buffer_pos_ + bytes > buffer_end_) - return growBuffer(bytes); - return true; - } - - void writeUint8(unsigned value) { - assert(value <= UCHAR_MAX); - *buffer_pos_++ = value; - } - void writeFloat(float value) { - *(float *)buffer_pos_ = value; - buffer_pos_ += sizeof(float); - } - void writeInt32(int value) { - *(int *)buffer_pos_ = value; - buffer_pos_ += sizeof(int); - } - void writeUint32(unsigned value) { - *(unsigned *)buffer_pos_ = value; - buffer_pos_ += sizeof(unsigned); - } - void writeOp(Opcode op) { - if (oom_) - return; - - writeUint8(op); - for (int i = 0; i < OpcodeInfo[op].nuses; i++) - oopMap_.pop(); - for (int i = 0; i < OpcodeInfo[op].ndefs; i++) - oopMap_.append(false); - if (oopMap_.length() > maxStackDepth_) - maxStackDepth_ = oopMap_.length(); - } - void writeObject(Object *obj) { - assert(obj); - - writeUint32(objects_.length()); - if (!objects_.append(obj)) - oom_ = true; - } - - int position() const { - assert(buffer_pos_ - buffer_ < INT_MAX / 2); - return int(buffer_pos_ - buffer_); - } - - public: - BytecodeEmitter(); - ~BytecodeEmitter(); - - void accept(VirtualObjectVisitor *visitor); - - bytecode *buffer() const { - return buffer_; - } - unsigned length() const { - return unsigned(buffer_pos_ - buffer_); - } - bool oom() const { - return oom_ || sourcemap_.oom(); - } - unsigned stackDepth() const { - return oopMap_.length(); - } - unsigned maxStackDepth() const { - return maxStackDepth_; - } - - void drop(unsigned amt) { - assert(amt <= stackDepth()); - for (unsigned i = 0; i < amt; i++) - oopMap_.pop(); - } - - void note_position(const SourcePosition &pos); - void setFirstLine(unsigned line) { - assert(!sourcemap_.length()); - firstLine_ = line; - } - - public: - // A label represents a local control-flow target in the bytecode. A label - // may be in three states: - // - // (1) Empty - the label is not used yet. - // (2) Pending - the label has incoming jumps, but the actual location of - // is not yet known. The list of jumps is threaded through - // the four bytes reserved for each emitted jump target. In - // this case, the offset field is the location of the most - // recently emitted incoming jump. - // (3) Bound - the label's position has been pinned. - // - // A jump can be emitted to a label in any state. Binding a label - // immediately backpatches all pending incoming jumps. A label must be - // bound if it is used. - class Label - { - int offset_ : 31; - bool bound_ : 1; - - void setOffset(int offset) { - offset_ = offset; - assert(offset_ == offset); - } - - public: - static const int INVALID_OFFSET = -1; - - public: - Label() - : offset_(INVALID_OFFSET), - bound_(false) - { - } - ~Label() - { - assert(bound_ || offset_ == INVALID_OFFSET); - } - - bool bound() const { - return bound_; - } - int offset() const { - return offset_; - } - void bind(int offset) { - assert(!bound_); - setOffset(offset); - bound_ = true; - } - int link(int offset) { - assert(!bound_); - int old = offset_; - setOffset(offset); - return old; - } - }; - - private: - int patchJumpAt(int at, int to) { - assert(at >= 0 && at < position()); - int old = *(int *)(buffer_ + at + 1); - *(int *)(buffer_ + at + 1) = (to - at); - return old; - } - - void singleByteOp(Opcode op) { - assert(OpcodeInfo[op].length == 1); - ensure(OpcodeInfo[op].length); - writeOp(op); - } - - // Mark a stack slot as having an object pointer. - void markOop(unsigned depth, bool oop = true) { - if (oom_) - return; - oopMap_[oopMap_.length() - depth] = oop; - } - bool getOop(unsigned depth) { - if (oom_) - return false; - return oopMap_[oopMap_.length() - depth]; - } - - // Mark the current bytecode position as either re-entrant (meaning, it - // can leave the interpreter or call to another function), or causing GC. - // This will record which stack slots have objects and associate that - // information with the current pc. - void recordOops(); - - public: - void int_(int value) { - ensure(OP_INT); - writeOp(OP_INT); - writeInt32(value); - } - void return_() { - ensure(OP_RETURN); - writeOp(OP_RETURN); - } - void getlocal(VariableSymbol *var); - void setlocal(VariableSymbol *var); - void getarg(VariableSymbol *var); - void setarg(VariableSymbol *var); - - void j(Opcode op, Label *label) { - // Since jumps can skip around the stream, do not try to do anything - // in a potentially truncated state. - if (oom()) - return; - - assert(op == OP_JT || op == OP_JF || op == OP_JUMP || - op == OP_CONTINUE || op == OP_BREAK); - ensure(OP_JUMP_LENGTH); - - if (label->bound()) { - // We can immediately emit this jump. - int distance = label->offset() - position(); - writeOp(op); - writeInt32(distance); - } else { - // We can't emit this jump yet. Add a placeholder. - int old = label->link(position()); - writeOp(op); - writeInt32(old); - } - } - void bind(Label *label) { - if (oom()) - return; - - assert(!label->bound()); - - int offset = label->offset(); - while (offset != Label::INVALID_OFFSET) - offset = patchJumpAt(offset, position()); - - label->bind(position()); - } - void jump(Label *label) { - j(OP_JUMP, label); - } - - void binary(Opcode op) { - singleByteOp(op); - } - void unary(Opcode op) { - singleByteOp(op); - } - - void pop() { - singleByteOp(OP_POP); - } - void stop() { - singleByteOp(OP_STOP); - } - void call(Handle fun, unsigned argc) { - recordOops(); - - if (fun->isNative()) { - ensure(OP_CALLNATIVE_LENGTH); - writeOp(OP_CALLNATIVE); - writeUint32(argc); - drop(argc + 1); - } else { - ensure(OP_CALL_LENGTH); - writeOp(OP_CALL); - drop(argc + 1); - - if (fun->returnType()->isVoid()) - drop(1); - else if (fun->returnType()->isTraceable()) - markOop(1); - } - } - - void getelem(Type *type) { - singleByteOp(OP_GETELEM); - - if (type->isTraceable()) - markOop(1); - } - void setelem(Type *type) { - singleByteOp(OP_SETELEM); - } - - void getfield(unsigned index, Type *type) { - ensure(OP_GETFIELD_LENGTH); - writeOp(OP_GETFIELD); - writeUint32(index); - - if (type->isTraceable()) - markOop(1); - } - void setfield(unsigned index, Type *type) { - ensure(OP_SETFIELD_LENGTH); - writeOp(OP_SETFIELD); - writeUint32(index); - - if (type->isTraceable()) - markOop(1); - } - - int enterloop() { - int offset = position(); - - ensure(OP_ENTERLOOP); - writeOp(OP_ENTERLOOP); - writeUint8(0); - - loopDepth_++; - if (loopDepth_ > maxLoopDepth_) - maxLoopDepth_ = loopDepth_; - - return offset; - } - void leaveloop() { - singleByteOp(OP_LEAVELOOP); - - assert(loopDepth_); - loopDepth_--; - } - void setLoopUsesLifoStack(int pos) { - assert(pos >= 0 && pos < position()); - assert(*(buffer() + pos) == OP_ENTERLOOP); - *(buffer() + pos + 1) = 1; - } - - void float_(float value) { - ensure(OP_FLOAT_LENGTH); - writeOp(OP_FLOAT); - writeFloat(value); - } - void cvt_i2f() { - singleByteOp(OP_CVT_I2F); - } - void newarray(Opcode op, Handle map, Heap::Tenure tenure) { - assert(op == OP_NEWFIXED || - op == OP_NEWEMPTY || - op == OP_NEWSIZED); - - recordOops(); - ensure(OpcodeInfo[op].length); - writeOp(op); - writeUint8(tenure); - writeObject(map); - - if (op == OP_NEWSIZED) - drop(map->type()->levels()); - - markOop(1); - } - void newstruct(Handle map, Heap::Tenure tenure) { - recordOops(); - ensure(OP_NEWSTRUCT_LENGTH); - writeOp(OP_NEWSTRUCT); - writeUint8(tenure); - writeObject(map); - markOop(1); - } - void deparray(ArrayMap *map) { - recordOops(); - ensure(OP_DEPARRAY_LENGTH); - writeOp(OP_DEPARRAY); - writeObject(map); - markOop(1); - } - void savelifo(unsigned slot) { - ensure(OP_SAVELIFO_LENGTH); - writeOp(OP_SAVELIFO); - writeUint32(slot); - } - void unwindlifo(unsigned slot) { - ensure(OP_UNWINDLIFO_LENGTH); - writeOp(OP_UNWINDLIFO); - writeUint32(slot); - } - - void newref(VariableSymbol *var); - - void slotref_lifo() { - recordOops(); - singleByteOp(OP_SLOTREF_LIFO); - markOop(1); - } - void getref(Type *type) { - if (type->pod() == PrimitiveType_Int32) { - singleByteOp(OP_GETREF_I); - } else { - assert(type->pod() == PrimitiveType_Float); - singleByteOp(OP_GETREF_F); - } - } - void setref(Type *type) { - if (type->pod() == PrimitiveType_Int32) { - singleByteOp(OP_SETREF_I); - } else { - assert(type->pod() == PrimitiveType_Float); - singleByteOp(OP_SETREF_F); - } - } - void object(Handle obj) { - assert(obj); - - ensure(OP_OBJECT_LENGTH); - writeOp(OP_OBJECT); - writeObject(obj); - markOop(1); - } - void duparray() { - singleByteOp(OP_DUPARRAY); - markOop(1); - } - void copyarray() { - singleByteOp(OP_COPYARRAY); - markOop(1); - } - void copystruct() { - singleByteOp(OP_COPYSTRUCT); - markOop(1); - } - void sizeof_() { - singleByteOp(OP_SIZEOF); - } - - void dup() { - bool oop = getOop(1); - singleByteOp(OP_DUP); - markOop(1, oop); - } - void dup2() { - bool oop2 = getOop(2); - bool oop1 = getOop(1); - - singleByteOp(OP_DUP2); - - markOop(2, oop2); - markOop(1, oop1); - } - void swap() { - bool oop2 = getOop(2); - bool oop1 = getOop(1); - - singleByteOp(OP_SWAP); - - markOop(2, oop1); - markOop(1, oop2); - } - void roll3() { - bool oop3 = getOop(3); - bool oop2 = getOop(2); - bool oop1 = getOop(1); - - singleByteOp(OP_ROLL3); - - markOop(3, oop2); - markOop(2, oop1); - markOop(1, oop3); - } - void pick2() { - bool oop2 = getOop(2); - bool oop1 = getOop(1); - - singleByteOp(OP_PICK2); - - markOop(3, oop2); - markOop(2, oop1); - markOop(1, oop2); - } - void pick3() { - bool oop3 = getOop(3); - bool oop2 = getOop(2); - bool oop1 = getOop(1); - - singleByteOp(OP_PICK3); - - markOop(4, oop3); - markOop(3, oop2); - markOop(2, oop1); - markOop(1, oop3); - } - - void returnvoid() { - singleByteOp(OP_RETURNVOID); - } - void bitnot() { - singleByteOp(OP_BITNOT); - } - void not_() { - singleByteOp(OP_NOT); - } - void not_f() { - singleByteOp(OP_NOT_F); - } - void neg() { - singleByteOp(OP_NEG); - } - void neg_f() { - singleByteOp(OP_NEG_F); - } - void true_() { - singleByteOp(OP_TRUE); - } - void false_() { - singleByteOp(OP_FALSE); - } - - unsigned addString(Handle name) { - unsigned index; - if (!strings_.add(name, &index)) { - oom_ |= true; - return 0; - } - return index; - } - - void getglobal(Symbol *sym); - void setglobal(Symbol *sym); - - void public_() { - recordOops(); - singleByteOp(OP_PUBLIC); - } - - void bitcast(Type *type) { - ensure(OP_BITCAST_LENGTH); - writeOp(OP_BITCAST); - writeObject(type); - } - - void tableswitch(int low, int high, Label *def, Label **labels) { - unsigned tablesize = TableSwitchEntries(low, high); - assert(tablesize <= MAX_TABLESWITCH_ENTRIES); - - ensure(OP_TABLESWITCH_LENGTH); - writeOp(OP_TABLESWITCH); - writeInt32(low); - writeInt32(high); - jump(def); - - // When labels are patched, we assume each position is a jump. Rather - // than hack position offsets to make labels work without actual - // incoming jumps, we emit a jump inline. - for (unsigned i = 0; i < tablesize; i++) - jump(labels[i]); - } - - void import(unsigned index) { - ensure(OP_IMPORT_LENGTH); - writeOp(OP_IMPORT); - writeUint32(index); - markOop(1); - } - - bool allocate(VariableSymbol *sym); - - Code *finish(); - - // Helpers. -#if 0 - void get(Variable *var) { - if (var->location() == Variable::STACK) { - getlocal(var); - } else { - assert(var->location() == Variable::PARAMETER); - getarg(var); - } - } - void set(Variable *var) { - assert(!var->type()->isReference()); - if (var->location() == Variable::STACK) { - setlocal(var); - } else { - assert(var->location() == Variable::PARAMETER); - setarg(var); - } - } -#endif -}; - -} - -#endif // _include_bytecode_emitter_h_ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_bytecode_emitter_h_ +#define _include_bytecode_emitter_h_ + +#include +#include +#include "../Opcodes.h" +#include "../Handles.h" +#include "../CompactBuffer.h" +#include "../Array.h" +#include "../HashTable.h" +#include "../VirtualObjectVisitor.h" + +namespace ke { + +// An overarching note on overflows: Though we should use CheckedInt, we rely +// on a source-length cap covering all possible overflow checks. As long as +// the source length is capped to INT_MAX, the number of AST nodes will be +// strictly beneath that cap, and so to will most things generated by the +// AST. For example, local and argument counts, number of functions, fields +// in a struct, et cetera. Not everything can be covered by this cap: the +// stack depth, number of bytecodes for example. In these cases we explicitly +// test for overflow of INT_MAX and in other cases we assert. +// +// In the future, we should of course use a checked int wrapper. + +class Type; +class Map; +class ArrayMap; +class Symbol; +class VariableSymbol; + +struct OopRecord +{ + unsigned depth; + unsigned offset; + Vector slots; + + OopRecord(unsigned length, unsigned offset) + : depth(length), + offset(offset) + { + } +}; + +class StringTable +{ + struct Entry + { + String *string; + unsigned index; + + Entry() + : string(NULL), + index(0) + { + } + + Entry(String *string, unsigned index) + : string(string), + index(index) + { + } + }; + + struct Policy + { + typedef Entry Payload; + + static uint32 hash(String *string) { + return HashPointer(string); + } + + static bool matches(String *key, Entry &e) { + return key == e.string; + } + }; + + typedef HashTable Set; + + public: + StringTable(); + bool initialize(); + void accept(VirtualObjectVisitor *visitor); + + bool add(Handle name, unsigned *indexp); + size_t stringCount() const { + return strings_.length(); + } + String *stringAt(size_t i) const { + return strings_[i].string; + } + + private: + Set set_; + Vector strings_; + bool initialized_; +}; + +// The bytecode emitter assists in building Code objects, by emitting opcodes +// and tracking stack information. Since the SemA creates temporary pool/root +// scopes, we cannot use ScopedRoot or PoolObjects. Instead, BCE gets a +// special marking function. +class BytecodeEmitter : public AutoRooted +{ + private: + bytecode *buffer_; + bytecode *buffer_pos_; + bytecode *buffer_end_; + bytecode inline_buffer_[512]; + bool oom_; + unsigned maxStackDepth_; + Vector oopMap_; + Vector oopRecords_; + CompactBufferWriter sourcemap_; + unsigned lastCodePosition_; + unsigned firstLine_; + StringTable strings_; + Vector locals_; + Vector objects_; + unsigned loopDepth_; + unsigned maxLoopDepth_; + + private: + // The maximum size of a code object must be such that the distance between + // two instructions fits into a signed, 31-bit integer. This ensures that + // all code-bounded buffers (like the number of object literals or + // safepoints) does not need to be bounds checked. It also means we can add + // any two offsets and be guaranteed that they don't overflow. + static const size_t MAX_LENGTH = INT_MAX / 2; + + private: + bool growBuffer(size_t bytes); + + // Checking the return value is not necessary, since the inline buffer is + // used as a ballast. + bool ensure(size_t bytes) { + if (buffer_pos_ + bytes > buffer_end_) + return growBuffer(bytes); + return true; + } + + void writeUint8(unsigned value) { + assert(value <= UCHAR_MAX); + *buffer_pos_++ = value; + } + void writeFloat(float value) { + *(float *)buffer_pos_ = value; + buffer_pos_ += sizeof(float); + } + void writeInt32(int value) { + *(int *)buffer_pos_ = value; + buffer_pos_ += sizeof(int); + } + void writeUint32(unsigned value) { + *(unsigned *)buffer_pos_ = value; + buffer_pos_ += sizeof(unsigned); + } + void writeOp(Opcode op) { + if (oom_) + return; + + writeUint8(op); + for (int i = 0; i < OpcodeInfo[op].nuses; i++) + oopMap_.pop(); + for (int i = 0; i < OpcodeInfo[op].ndefs; i++) + oopMap_.append(false); + if (oopMap_.length() > maxStackDepth_) + maxStackDepth_ = oopMap_.length(); + } + void writeObject(Object *obj) { + assert(obj); + + writeUint32(objects_.length()); + if (!objects_.append(obj)) + oom_ = true; + } + + int position() const { + assert(buffer_pos_ - buffer_ < INT_MAX / 2); + return int(buffer_pos_ - buffer_); + } + + public: + BytecodeEmitter(); + ~BytecodeEmitter(); + + void accept(VirtualObjectVisitor *visitor); + + bytecode *buffer() const { + return buffer_; + } + unsigned length() const { + return unsigned(buffer_pos_ - buffer_); + } + bool oom() const { + return oom_ || sourcemap_.oom(); + } + unsigned stackDepth() const { + return oopMap_.length(); + } + unsigned maxStackDepth() const { + return maxStackDepth_; + } + + void drop(unsigned amt) { + assert(amt <= stackDepth()); + for (unsigned i = 0; i < amt; i++) + oopMap_.pop(); + } + + void note_position(const SourcePosition &pos); + void setFirstLine(unsigned line) { + assert(!sourcemap_.length()); + firstLine_ = line; + } + + public: + // A label represents a local control-flow target in the bytecode. A label + // may be in three states: + // + // (1) Empty - the label is not used yet. + // (2) Pending - the label has incoming jumps, but the actual location of + // is not yet known. The list of jumps is threaded through + // the four bytes reserved for each emitted jump target. In + // this case, the offset field is the location of the most + // recently emitted incoming jump. + // (3) Bound - the label's position has been pinned. + // + // A jump can be emitted to a label in any state. Binding a label + // immediately backpatches all pending incoming jumps. A label must be + // bound if it is used. + class Label + { + int offset_ : 31; + bool bound_ : 1; + + void setOffset(int offset) { + offset_ = offset; + assert(offset_ == offset); + } + + public: + static const int INVALID_OFFSET = -1; + + public: + Label() + : offset_(INVALID_OFFSET), + bound_(false) + { + } + ~Label() + { + assert(bound_ || offset_ == INVALID_OFFSET); + } + + bool bound() const { + return bound_; + } + int offset() const { + return offset_; + } + void bind(int offset) { + assert(!bound_); + setOffset(offset); + bound_ = true; + } + int link(int offset) { + assert(!bound_); + int old = offset_; + setOffset(offset); + return old; + } + }; + + private: + int patchJumpAt(int at, int to) { + assert(at >= 0 && at < position()); + int old = *(int *)(buffer_ + at + 1); + *(int *)(buffer_ + at + 1) = (to - at); + return old; + } + + void singleByteOp(Opcode op) { + assert(OpcodeInfo[op].length == 1); + ensure(OpcodeInfo[op].length); + writeOp(op); + } + + // Mark a stack slot as having an object pointer. + void markOop(unsigned depth, bool oop = true) { + if (oom_) + return; + oopMap_[oopMap_.length() - depth] = oop; + } + bool getOop(unsigned depth) { + if (oom_) + return false; + return oopMap_[oopMap_.length() - depth]; + } + + // Mark the current bytecode position as either re-entrant (meaning, it + // can leave the interpreter or call to another function), or causing GC. + // This will record which stack slots have objects and associate that + // information with the current pc. + void recordOops(); + + public: + void int_(int value) { + ensure(OP_INT); + writeOp(OP_INT); + writeInt32(value); + } + void return_() { + ensure(OP_RETURN); + writeOp(OP_RETURN); + } + void getlocal(VariableSymbol *var); + void setlocal(VariableSymbol *var); + void getarg(VariableSymbol *var); + void setarg(VariableSymbol *var); + + void j(Opcode op, Label *label) { + // Since jumps can skip around the stream, do not try to do anything + // in a potentially truncated state. + if (oom()) + return; + + assert(op == OP_JT || op == OP_JF || op == OP_JUMP || + op == OP_CONTINUE || op == OP_BREAK); + ensure(OP_JUMP_LENGTH); + + if (label->bound()) { + // We can immediately emit this jump. + int distance = label->offset() - position(); + writeOp(op); + writeInt32(distance); + } else { + // We can't emit this jump yet. Add a placeholder. + int old = label->link(position()); + writeOp(op); + writeInt32(old); + } + } + void bind(Label *label) { + if (oom()) + return; + + assert(!label->bound()); + + int offset = label->offset(); + while (offset != Label::INVALID_OFFSET) + offset = patchJumpAt(offset, position()); + + label->bind(position()); + } + void jump(Label *label) { + j(OP_JUMP, label); + } + + void binary(Opcode op) { + singleByteOp(op); + } + void unary(Opcode op) { + singleByteOp(op); + } + + void pop() { + singleByteOp(OP_POP); + } + void stop() { + singleByteOp(OP_STOP); + } + void call(Handle fun, unsigned argc) { + recordOops(); + + if (fun->isNative()) { + ensure(OP_CALLNATIVE_LENGTH); + writeOp(OP_CALLNATIVE); + writeUint32(argc); + drop(argc + 1); + } else { + ensure(OP_CALL_LENGTH); + writeOp(OP_CALL); + drop(argc + 1); + + if (fun->returnType()->isVoid()) + drop(1); + else if (fun->returnType()->isTraceable()) + markOop(1); + } + } + + void getelem(Type *type) { + singleByteOp(OP_GETELEM); + + if (type->isTraceable()) + markOop(1); + } + void setelem(Type *type) { + singleByteOp(OP_SETELEM); + } + + void getfield(unsigned index, Type *type) { + ensure(OP_GETFIELD_LENGTH); + writeOp(OP_GETFIELD); + writeUint32(index); + + if (type->isTraceable()) + markOop(1); + } + void setfield(unsigned index, Type *type) { + ensure(OP_SETFIELD_LENGTH); + writeOp(OP_SETFIELD); + writeUint32(index); + + if (type->isTraceable()) + markOop(1); + } + + int enterloop() { + int offset = position(); + + ensure(OP_ENTERLOOP); + writeOp(OP_ENTERLOOP); + writeUint8(0); + + loopDepth_++; + if (loopDepth_ > maxLoopDepth_) + maxLoopDepth_ = loopDepth_; + + return offset; + } + void leaveloop() { + singleByteOp(OP_LEAVELOOP); + + assert(loopDepth_); + loopDepth_--; + } + void setLoopUsesLifoStack(int pos) { + assert(pos >= 0 && pos < position()); + assert(*(buffer() + pos) == OP_ENTERLOOP); + *(buffer() + pos + 1) = 1; + } + + void float_(float value) { + ensure(OP_FLOAT_LENGTH); + writeOp(OP_FLOAT); + writeFloat(value); + } + void cvt_i2f() { + singleByteOp(OP_CVT_I2F); + } + void newarray(Opcode op, Handle map, Heap::Tenure tenure) { + assert(op == OP_NEWFIXED || + op == OP_NEWEMPTY || + op == OP_NEWSIZED); + + recordOops(); + ensure(OpcodeInfo[op].length); + writeOp(op); + writeUint8(tenure); + writeObject(map); + + if (op == OP_NEWSIZED) + drop(map->type()->levels()); + + markOop(1); + } + void newstruct(Handle map, Heap::Tenure tenure) { + recordOops(); + ensure(OP_NEWSTRUCT_LENGTH); + writeOp(OP_NEWSTRUCT); + writeUint8(tenure); + writeObject(map); + markOop(1); + } + void deparray(ArrayMap *map) { + recordOops(); + ensure(OP_DEPARRAY_LENGTH); + writeOp(OP_DEPARRAY); + writeObject(map); + markOop(1); + } + void savelifo(unsigned slot) { + ensure(OP_SAVELIFO_LENGTH); + writeOp(OP_SAVELIFO); + writeUint32(slot); + } + void unwindlifo(unsigned slot) { + ensure(OP_UNWINDLIFO_LENGTH); + writeOp(OP_UNWINDLIFO); + writeUint32(slot); + } + + void newref(VariableSymbol *var); + + void slotref_lifo() { + recordOops(); + singleByteOp(OP_SLOTREF_LIFO); + markOop(1); + } + void getref(Type *type) { + if (type->pod() == PrimitiveType_Int32) { + singleByteOp(OP_GETREF_I); + } else { + assert(type->pod() == PrimitiveType_Float); + singleByteOp(OP_GETREF_F); + } + } + void setref(Type *type) { + if (type->pod() == PrimitiveType_Int32) { + singleByteOp(OP_SETREF_I); + } else { + assert(type->pod() == PrimitiveType_Float); + singleByteOp(OP_SETREF_F); + } + } + void object(Handle obj) { + assert(obj); + + ensure(OP_OBJECT_LENGTH); + writeOp(OP_OBJECT); + writeObject(obj); + markOop(1); + } + void duparray() { + singleByteOp(OP_DUPARRAY); + markOop(1); + } + void copyarray() { + singleByteOp(OP_COPYARRAY); + markOop(1); + } + void copystruct() { + singleByteOp(OP_COPYSTRUCT); + markOop(1); + } + void sizeof_() { + singleByteOp(OP_SIZEOF); + } + + void dup() { + bool oop = getOop(1); + singleByteOp(OP_DUP); + markOop(1, oop); + } + void dup2() { + bool oop2 = getOop(2); + bool oop1 = getOop(1); + + singleByteOp(OP_DUP2); + + markOop(2, oop2); + markOop(1, oop1); + } + void swap() { + bool oop2 = getOop(2); + bool oop1 = getOop(1); + + singleByteOp(OP_SWAP); + + markOop(2, oop1); + markOop(1, oop2); + } + void roll3() { + bool oop3 = getOop(3); + bool oop2 = getOop(2); + bool oop1 = getOop(1); + + singleByteOp(OP_ROLL3); + + markOop(3, oop2); + markOop(2, oop1); + markOop(1, oop3); + } + void pick2() { + bool oop2 = getOop(2); + bool oop1 = getOop(1); + + singleByteOp(OP_PICK2); + + markOop(3, oop2); + markOop(2, oop1); + markOop(1, oop2); + } + void pick3() { + bool oop3 = getOop(3); + bool oop2 = getOop(2); + bool oop1 = getOop(1); + + singleByteOp(OP_PICK3); + + markOop(4, oop3); + markOop(3, oop2); + markOop(2, oop1); + markOop(1, oop3); + } + + void returnvoid() { + singleByteOp(OP_RETURNVOID); + } + void bitnot() { + singleByteOp(OP_BITNOT); + } + void not_() { + singleByteOp(OP_NOT); + } + void not_f() { + singleByteOp(OP_NOT_F); + } + void neg() { + singleByteOp(OP_NEG); + } + void neg_f() { + singleByteOp(OP_NEG_F); + } + void true_() { + singleByteOp(OP_TRUE); + } + void false_() { + singleByteOp(OP_FALSE); + } + + unsigned addString(Handle name) { + unsigned index; + if (!strings_.add(name, &index)) { + oom_ |= true; + return 0; + } + return index; + } + + void getglobal(Symbol *sym); + void setglobal(Symbol *sym); + + void public_() { + recordOops(); + singleByteOp(OP_PUBLIC); + } + + void bitcast(Type *type) { + ensure(OP_BITCAST_LENGTH); + writeOp(OP_BITCAST); + writeObject(type); + } + + void tableswitch(int low, int high, Label *def, Label **labels) { + unsigned tablesize = TableSwitchEntries(low, high); + assert(tablesize <= MAX_TABLESWITCH_ENTRIES); + + ensure(OP_TABLESWITCH_LENGTH); + writeOp(OP_TABLESWITCH); + writeInt32(low); + writeInt32(high); + jump(def); + + // When labels are patched, we assume each position is a jump. Rather + // than hack position offsets to make labels work without actual + // incoming jumps, we emit a jump inline. + for (unsigned i = 0; i < tablesize; i++) + jump(labels[i]); + } + + void import(unsigned index) { + ensure(OP_IMPORT_LENGTH); + writeOp(OP_IMPORT); + writeUint32(index); + markOop(1); + } + + bool allocate(VariableSymbol *sym); + + Code *finish(); + + // Helpers. +#if 0 + void get(Variable *var) { + if (var->location() == Variable::STACK) { + getlocal(var); + } else { + assert(var->location() == Variable::PARAMETER); + getarg(var); + } + } + void set(Variable *var) { + assert(!var->type()->isReference()); + if (var->location() == Variable::STACK) { + setlocal(var); + } else { + assert(var->location() == Variable::PARAMETER); + setarg(var); + } + } +#endif +}; + +} + +#endif // _include_bytecode_emitter_h_ diff -r c4b1341297e5 -r ba85a47ee414 src/compiler/CompileContext.h --- a/src/compiler/CompileContext.h Mon Jun 10 00:46:29 2013 +0100 +++ b/src/compiler/CompileContext.h Mon Jun 10 01:04:43 2013 +0100 @@ -1,170 +1,170 @@ -/* - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#ifndef _include_jitcraft_compile_context_h_ -#define _include_jitcraft_compile_context_h_ - -#include -#include -#include "../Messages.h" -#include "../PoolAllocator.h" -#include "../Handles.h" -#include "../Modules.h" -#include "../Packages.h" - -namespace ke { - -class Zone; -class ParseTree; -class String; -class ImportScope; -class GlobalScope; -struct SourcePosition; -struct TranslationUnit; -class ImportStatement; - -struct CompileError -{ - CompileError() - : message(NULL) - { - } - CompileError(unsigned line, unsigned col, const char *message) - : line(line), - col(col), - message(message) - { - } - - unsigned line; - unsigned col; - const char *message; -}; - -struct TranslationUnit : public PoolObject -{ - TranslationUnit(Handle module) - : tree_(NULL), - importScope_(NULL), - globalScope_(NULL), - module_(module), - enoughMemory_(true), - dependencyFailed_(false) - { - } - - void destroy(); - - ParseTree *tree_; - PoolList dependents_; - ImportScope *importScope_; - GlobalScope *globalScope_; - ScopedRoot module_; - - ParseTree *tree() const { - return tree_; - } - PoolList &dependents() { - return dependents_; - } - bool failed() const { - return errors_.length() || - !enoughMemory_ || - dependencyFailed_; - } - void setImportScope(ImportScope *scope) { - assert(!importScope_); - importScope_ = scope; - } - void setGlobalScope(GlobalScope *scope) { - assert(!globalScope_); - globalScope_ = scope; - } - ImportScope *importScope() const { - return importScope_; - } - GlobalScope *globalScope() const { - return globalScope_; - } - Handle module() { - return module_; - } - - void setDependencyFailed() { - dependencyFailed_ = true; - } - void setOutOfMemory() { - enoughMemory_ = false; - } - void appendError(const char *report, unsigned line, unsigned col) { - enoughMemory_ |= errors_.append(CompileError(line, col, report)); - } - const Vector *errors() { - return &errors_; - } - - private: - bool enoughMemory_; - bool dependencyFailed_; - Vector errors_; -}; - -class CompileContext -{ - Zone *zone_; - Vector units_; - Vector unparsed_; - TranslationUnit *active_; - - private: - char *loadSource(Handle module, size_t *length); - bool parse(TranslationUnit *unit); - bool evaluateImports(TranslationUnit *unit); - bool propagateFailure(); - bool add(Handle module); - - public: - CompileContext(Zone *zone); - ~CompileContext(); - - bool initialize(Handle package, const char *name); - bool compile(); - void enter(TranslationUnit *unit); - void leave(); - - void reportErrorVa(const SourcePosition &pos, Message msg, va_list ap); - void reportError(const SourcePosition &pos, Message msg, ...); -}; - -class AutoEnterCompileContext -{ - CompileContext &cc_; - - public: - AutoEnterCompileContext(CompileContext &cc, TranslationUnit *unit) - : cc_(cc) - { - cc_.enter(unit); - } - ~AutoEnterCompileContext() { - cc_.leave(); - } -}; - -} // namespace ke - -#endif // _include_jitcraft_compile_context_h_ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_compile_context_h_ +#define _include_jitcraft_compile_context_h_ + +#include +#include +#include "../Messages.h" +#include "../PoolAllocator.h" +#include "../Handles.h" +#include "../Modules.h" +#include "../Packages.h" + +namespace ke { + +class Zone; +class ParseTree; +class String; +class ImportScope; +class GlobalScope; +struct SourcePosition; +struct TranslationUnit; +class ImportStatement; + +struct CompileError +{ + CompileError() + : message(NULL) + { + } + CompileError(unsigned line, unsigned col, const char *message) + : line(line), + col(col), + message(message) + { + } + + unsigned line; + unsigned col; + const char *message; +}; + +struct TranslationUnit : public PoolObject +{ + TranslationUnit(Handle module) + : tree_(NULL), + importScope_(NULL), + globalScope_(NULL), + module_(module), + enoughMemory_(true), + dependencyFailed_(false) + { + } + + void destroy(); + + ParseTree *tree_; + PoolList dependents_; + ImportScope *importScope_; + GlobalScope *globalScope_; + ScopedRoot module_; + + ParseTree *tree() const { + return tree_; + } + PoolList &dependents() { + return dependents_; + } + bool failed() const { + return errors_.length() || + !enoughMemory_ || + dependencyFailed_; + } + void setImportScope(ImportScope *scope) { + assert(!importScope_); + importScope_ = scope; + } + void setGlobalScope(GlobalScope *scope) { + assert(!globalScope_); + globalScope_ = scope; + } + ImportScope *importScope() const { + return importScope_; + } + GlobalScope *globalScope() const { + return globalScope_; + } + Handle module() { + return module_; + } + + void setDependencyFailed() { + dependencyFailed_ = true; + } + void setOutOfMemory() { + enoughMemory_ = false; + } + void appendError(const char *report, unsigned line, unsigned col) { + enoughMemory_ |= errors_.append(CompileError(line, col, report)); + } + const Vector *errors() { + return &errors_; + } + + private: + bool enoughMemory_; + bool dependencyFailed_; + Vector errors_; +}; + +class CompileContext +{ + Zone *zone_; + Vector units_; + Vector unparsed_; + TranslationUnit *active_; + + private: + char *loadSource(Handle module, size_t *length); + bool parse(TranslationUnit *unit); + bool evaluateImports(TranslationUnit *unit); + bool propagateFailure(); + bool add(Handle module); + + public: + CompileContext(Zone *zone); + ~CompileContext(); + + bool initialize(Handle package, const char *name); + bool compile(); + void enter(TranslationUnit *unit); + void leave(); + + void reportErrorVa(const SourcePosition &pos, Message msg, va_list ap); + void reportError(const SourcePosition &pos, Message msg, ...); +}; + +class AutoEnterCompileContext +{ + CompileContext &cc_; + + public: + AutoEnterCompileContext(CompileContext &cc, TranslationUnit *unit) + : cc_(cc) + { + cc_.enter(unit); + } + ~AutoEnterCompileContext() { + cc_.leave(); + } +}; + +} // namespace ke + +#endif // _include_jitcraft_compile_context_h_ diff -r c4b1341297e5 -r ba85a47ee414 src/compiler/ConstantEvaluator.cpp --- a/src/compiler/ConstantEvaluator.cpp Mon Jun 10 00:46:29 2013 +0100 +++ b/src/compiler/ConstantEvaluator.cpp Mon Jun 10 01:04:43 2013 +0100 @@ -1,94 +1,94 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#include "AST.h" -#include "ConstantEvaluator.h" -#include "../Heap-inl.h" -#include "../Handles-inl.h" - -using namespace ke; - -static inline bool -EvaluateBinaryOperation(TokenKind kind, const BoxedPrimitive &left, const BoxedPrimitive &right, - BoxedPrimitive *out) -{ - switch (kind) { - case TOK_PLUS: - *out = BoxedPrimitive::Int(left.toInt() + right.toInt()); - return true; - - case TOK_SHL: - *out = BoxedPrimitive::Int(left.toInt() << right.toInt()); - return true; - - default: - return false; - } -} - -static inline bool -EvaluateUnaryOperation(TokenKind kind, const BoxedPrimitive &inner, BoxedPrimitive *out) -{ - switch (kind) { - case TOK_MINUS: - *out = BoxedPrimitive::Int(-inner.toInt()); - return true; - - default: - return false; - } -} - -bool -ke::EvaluateForConstant(Expression *expr, BoxedPrimitive *out) -{ - switch (expr->kind()) { - case AstNode::kBinaryExpression: - { - BoxedPrimitive left; - BoxedPrimitive right; - BinaryExpression *b = expr->toBinaryExpression(); - if (!EvaluateForConstant(b->left(), &left) || - !EvaluateForConstant(b->right(), &right)) - { - return false; - } - return EvaluateBinaryOperation(b->token(), left, right, out); - } - - case AstNode::kUnaryExpression: - { - BoxedPrimitive inner; - UnaryExpression *u = expr->toUnaryExpression(); - if (!EvaluateForConstant(u->expression(), &inner)) - return false; - return EvaluateUnaryOperation(u->token(), inner, out); - } - - case AstNode::kBooleanLiteral: - *out = BoxedPrimitive::Int(expr->toBooleanLiteral()->token() == TOK_TRUE ? 1 : 0); - return true; - - case AstNode::kIntegerLiteral: - *out = BoxedPrimitive::Int(expr->toIntegerLiteral()->value()); - return true; - - default: - return false; - } -} +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#include "AST.h" +#include "ConstantEvaluator.h" +#include "../Heap-inl.h" +#include "../Handles-inl.h" + +using namespace ke; + +static inline bool +EvaluateBinaryOperation(TokenKind kind, const BoxedPrimitive &left, const BoxedPrimitive &right, + BoxedPrimitive *out) +{ + switch (kind) { + case TOK_PLUS: + *out = BoxedPrimitive::Int(left.toInt() + right.toInt()); + return true; + + case TOK_SHL: + *out = BoxedPrimitive::Int(left.toInt() << right.toInt()); + return true; + + default: + return false; + } +} + +static inline bool +EvaluateUnaryOperation(TokenKind kind, const BoxedPrimitive &inner, BoxedPrimitive *out) +{ + switch (kind) { + case TOK_MINUS: + *out = BoxedPrimitive::Int(-inner.toInt()); + return true; + + default: + return false; + } +} + +bool +ke::EvaluateForConstant(Expression *expr, BoxedPrimitive *out) +{ + switch (expr->kind()) { + case AstNode::kBinaryExpression: + { + BoxedPrimitive left; + BoxedPrimitive right; + BinaryExpression *b = expr->toBinaryExpression(); + if (!EvaluateForConstant(b->left(), &left) || + !EvaluateForConstant(b->right(), &right)) + { + return false; + } + return EvaluateBinaryOperation(b->token(), left, right, out); + } + + case AstNode::kUnaryExpression: + { + BoxedPrimitive inner; + UnaryExpression *u = expr->toUnaryExpression(); + if (!EvaluateForConstant(u->expression(), &inner)) + return false; + return EvaluateUnaryOperation(u->token(), inner, out); + } + + case AstNode::kBooleanLiteral: + *out = BoxedPrimitive::Int(expr->toBooleanLiteral()->token() == TOK_TRUE ? 1 : 0); + return true; + + case AstNode::kIntegerLiteral: + *out = BoxedPrimitive::Int(expr->toIntegerLiteral()->value()); + return true; + + default: + return false; + } +} diff -r c4b1341297e5 -r ba85a47ee414 src/compiler/ConstantEvaluator.h --- a/src/compiler/ConstantEvaluator.h Mon Jun 10 00:46:29 2013 +0100 +++ b/src/compiler/ConstantEvaluator.h Mon Jun 10 01:04:43 2013 +0100 @@ -1,32 +1,32 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#ifndef _include_sp2_constant_evaluator_h_ -#define _include_sp2_constant_evaluator_h_ - -#include "../Types.h" - -namespace ke { - -class Expression; - -bool EvaluateForConstant(Expression *expr, BoxedPrimitive *out); - -} - -#endif // _include_sp2_constant_evaluator_h_ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_sp2_constant_evaluator_h_ +#define _include_sp2_constant_evaluator_h_ + +#include "../Types.h" + +namespace ke { + +class Expression; + +bool EvaluateForConstant(Expression *expr, BoxedPrimitive *out); + +} + +#endif // _include_sp2_constant_evaluator_h_ diff -r c4b1341297e5 -r ba85a47ee414 src/compiler/HIR.cpp --- a/src/compiler/HIR.cpp Mon Jun 10 00:46:29 2013 +0100 +++ b/src/compiler/HIR.cpp Mon Jun 10 01:04:43 2013 +0100 @@ -1,556 +1,556 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of JITCraft. - * - * JITCraft is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * JITCraft. If not, see http://www.gnu.org/licenses/. - */ -#include "AST.h" -#include "HIR.h" -#include "BytecodeEmitter.h" -#include "../Strings.h" -#include "../Heap-inl.h" -#include "../Handles-inl.h" - -using namespace ke; - -class HIRTranslator : public HIRVisitor -{ - public: - HIRTranslator(Zone *zone, BytecodeEmitter &emitter) - : zone_(zone), - emitter_(emitter) - { - } - - void translate(HIR *hir) { - hir->accept(this); - } - - void visitBoolean(HBoolean *hir); - void visitFloat(HFloat *hir); - void visitInteger(HInteger *hir); - void visitArray(HArray *hir); - void visitCall(HCall *call); - void visitStore(HStore *store); - void visitUnary(HUnary *unary); - void visitBinary(HBinary *binary); - void visitLocal(HLocal *local); - void visitGlobal(HGlobal *global); - void visitConvert(HConvert *convert); - void visitIndex(HIndex *index); - void visitField(HField *field); - void visitPostIncDec(HPostIncDec *hir); - void visitToRef(HToRef *hir); - void visitNewDependentArray(HNewDependentArray *hir); - void visitImport(HImport *import); - - private: - Zone *zone_; - BytecodeEmitter &emitter_; -}; - -#define __ emitter_. - -void -HIRTranslator::visitBoolean(HBoolean *hir) -{ - if (hir->value()) - __ true_(); - else - __ false_(); -} - -void -HIRTranslator::visitFloat(HFloat *hir) -{ - __ float_(hir->value()); -} - -void -HIRTranslator::visitInteger(HInteger *hir) -{ - __ int_(hir->value()); -} - -void -HIRTranslator::visitArray(HArray *hir) -{ - __ object(hir->array()); - - if (hir->duplicate()) - __ duparray(); -} - -void -HIRTranslator::visitCall(HCall *call) -{ - Local fun(zone_, FunctionType::cast(call->callee()->type())); - - assert(call->args()->length() == fun->parameters()->length()); - - __ note_position(call->node()->pos()); - - unsigned argc = call->args()->length(); - for (size_t i = call->args()->length() - 1; i < call->args()->length(); i--) { - HIR *hir = call->args()->at(i); - hir->accept(this); - } - - call->callee()->accept(this); - __ call(fun, argc); -} - -void -HGlobal::bind(HIRVisitor *visitor, BytecodeEmitter &emitter_) -{ -} - -void -HGlobal::hold(BytecodeEmitter &emitter_) -{ -} - -void -HGlobal::swapAndPick(BytecodeEmitter &emitter_) -{ - __ dup(); -} - -void -HGlobal::store(BytecodeEmitter &emitter_) -{ - __ setglobal(sym_); -} - -void -HGlobal::load(BytecodeEmitter &emitter_) -{ - __ getglobal(sym_); -} - -bool -HGlobal::ref(BytecodeEmitter &emitter_) -{ - return false; -} - -static inline Type * -MaybeDereference(Type *type) -{ - if (type->isReference()) - return ReferenceType::cast(type)->contained(); - return type; -} - -HLocal::HLocal(AstNode *node, VariableSymbol *sym) - : HLValue(node, MaybeDereference(sym->type())), - sym_(sym) -{ -} - -void -HLocal::bind(HIRVisitor *visitor, BytecodeEmitter &emitter_) -{ - if (sym_->type()->isReference()) { - if (sym_->storage() == VariableSymbol::Local) - __ getlocal(sym_); - else if (sym_->storage() == VariableSymbol::Arg) - __ getarg(sym_); - else - assert(false); - } -} - -void -HLocal::hold(BytecodeEmitter &emitter_) -{ - if (sym_->type()->isReference()) - __ dup(); -} - -void -HLocal::swapAndPick(BytecodeEmitter &emitter_) -{ - if (sym_->type()->isReference()) { - __ swap(); - __ pick2(); - } else { - __ dup(); - } -} - -void -HLocal::store(BytecodeEmitter &emitter_) -{ - if (sym_->type()->isReference()) { - __ setref(ReferenceType::cast(sym_->type())->contained()); - } else { - if (sym_->storage() == VariableSymbol::Local) - __ setlocal(sym_); - else if (sym_->storage() == VariableSymbol::Arg) - __ setarg(sym_); - else - assert(false); - } -} - -void -HLocal::load(BytecodeEmitter &emitter_) -{ - if (sym_->type()->isReference()) - __ getref(ReferenceType::cast(sym_->type())->contained()); - else if (sym_->storage() == VariableSymbol::Local) - __ getlocal(sym_); - else if (sym_->storage() == VariableSymbol::Arg) - __ getarg(sym_); - else - assert(false); -} - -bool -HLocal::ref(BytecodeEmitter &emitter_) -{ - if (sym_->storage() == VariableSymbol::Heap) { - // Closing over a reference should never pass SemA. - assert(!sym_->type()->isReference()); - - // We don't support direct references to heap-allocated locals. This - // should probably be banned in SemA? But for now, just assert. :TODO: - assert(false); - return false; - } - - if (sym_->type()->isReference()) { - if (sym_->storage() == VariableSymbol::Local) - __ getlocal(sym_); - else if (sym_->storage() == VariableSymbol::Arg) - __ getarg(sym_); - else - assert(false); - } else { - __ newref(sym_); - } - - return true; -} - -void -HIndex::bind(HIRVisitor *visitor, BytecodeEmitter &emitter_) -{ - __ note_position(node()->pos()); - left_->accept(visitor); - right_->accept(visitor); -} - -void -HIndex::hold(BytecodeEmitter &emitter_) -{ - __ dup2(); -} - -void -HIndex::swapAndPick(BytecodeEmitter &emitter_) -{ - // BASE INDEX VALUE - __ roll3(); // INDEX VALUE BASE - __ roll3(); // VALUE BASE INDEX - __ pick3(); // VALUE BASE INDEX VALUE -} - -void -HIndex::store(BytecodeEmitter &emitter_) -{ - __ setelem(type()); -} - -void -HIndex::load(BytecodeEmitter &emitter_) -{ - __ getelem(type()); -} - -bool -HIndex::ref(BytecodeEmitter &emitter_) -{ - return false; -} - -void -HField::bind(HIRVisitor *visitor, BytecodeEmitter &emitter_) -{ - __ note_position(node()->pos()); - base_->accept(visitor); -} - -void -HField::hold(BytecodeEmitter &emitter_) -{ - __ dup(); -} - -void -HField::swapAndPick(BytecodeEmitter &emitter_) -{ - // BASE VALUE - __ swap(); // VALUE BASE - __ pick2(); // VALUE BASE VALUE -} - -void -HField::store(BytecodeEmitter &emitter_) -{ - __ setfield(index_, type()); -} - -void -HField::load(BytecodeEmitter &emitter_) -{ - __ getfield(index_, type()); -} - -bool -HField::ref(BytecodeEmitter &emitter_) -{ - return false; -} - -void -HIRTranslator::visitLocal(HLocal *local) -{ - local->bind(this, emitter_); - local->load(emitter_); -} - -void -HIRTranslator::visitIndex(HIndex *index) -{ - index->bind(this, emitter_); - index->load(emitter_); -} - -void -HIRTranslator::visitField(HField *field) -{ - field->bind(this, emitter_); - field->load(emitter_); -} - -void -HIRTranslator::visitGlobal(HGlobal *global) -{ - global->bind(this, emitter_); - global->load(emitter_); -} - -static inline Opcode -TokenToOpcode(TokenKind kind) -{ - switch (kind) { - case TOK_PLUS: - case TOK_ASSIGN_ADD: - case TOK_INCREMENT: - return OP_ADD; - case TOK_MINUS: - case TOK_ASSIGN_SUB: - case TOK_DECREMENT: - return OP_SUB; - case TOK_STAR: - case TOK_ASSIGN_MUL: - return OP_MUL; - case TOK_SLASH: - case TOK_ASSIGN_DIV: - return OP_DIV; - case TOK_PERCENT: - case TOK_ASSIGN_MOD: - return OP_MOD; - case TOK_SHR: - case TOK_ASSIGN_SHR: - return OP_SHR; - case TOK_USHR: - case TOK_ASSIGN_USHR: - return OP_USHR; - case TOK_SHL: - case TOK_ASSIGN_SHL: - return OP_SHL; - case TOK_BITAND: - case TOK_ASSIGN_BITAND: - return OP_BITAND; - case TOK_BITOR: - case TOK_ASSIGN_BITOR: - return OP_BITOR; - case TOK_GT: - return OP_GT; - case TOK_GE: - return OP_GE; - case TOK_LT: - return OP_LT; - case TOK_LE: - return OP_LE; - case TOK_EQUALS: - return OP_EQ; - case TOK_NOTEQUALS: - return OP_NE; - default: - assert(kind == TOK_BITXOR); - return OP_BITXOR; - } -} - -void -HIRTranslator::visitStore(HStore *store) -{ - store->lval()->bind(this, emitter_); - - if (store->kind() == TOK_ASSIGN) { - // Fixed-length arrays and structs have copy semantics. - Local type(zone_, store->lval()->type()); - if (type->isArray() && ArrayType::cast(type)->isFixedLength()) { - store->lval()->load(emitter_); - store->rval()->accept(this); - __ copyarray(); - } else if (type->isStruct()) { - store->lval()->load(emitter_); - store->rval()->accept(this); - __ copystruct(); - } else { - store->rval()->accept(this); - store->lval()->store(emitter_); - } - } else { - // Advanced case: dup the ref, load, op, store. - store->lval()->hold(emitter_); - store->lval()->load(emitter_); - store->rval()->accept(this); - __ binary(TokenToOpcode(store->kind())); - store->lval()->store(emitter_); - } -} - -void -HIRTranslator::visitPostIncDec(HPostIncDec *hir) -{ - hir->lval()->bind(this, emitter_); - hir->lval()->hold(emitter_); - hir->lval()->load(emitter_); - hir->lval()->swapAndPick(emitter_); - hir->rval()->accept(this); - __ binary(TokenToOpcode(hir->kind())); - hir->lval()->store(emitter_); - __ pop(); -} - -void -HIRTranslator::visitBinary(HBinary *binary) -{ - binary->left()->accept(this); - binary->right()->accept(this); - - // We define three sets of math operators: - // - // (1) ADD, SUB, MUL, DIV, MOD, which take two equal types and produce - // the same type. - // (2) LT, LE, GE, GT, EQ, NE, which take two equal types and produce - // a boolean. - // (3) SHL, SR, USHR, BITAND, BITOR, BITXOR, BITNOT, which take two - // equal integer types and return that integer type. - // - Opcode op = TokenToOpcode(binary->token()); - - if (op == OP_DIV) - __ note_position(binary->node()->pos()); - - if (op >= OP_ADD && op <= OP_NE) { - if (binary->type()->isFloat()) - op = Opcode(int(op) + int(OP_ADD_F - OP_ADD)); - } else { - assert(binary->type()->isInt32()); - } - - __ binary(op); -} - -void -HIRTranslator::visitUnary(HUnary *unary) -{ - unary->in()->accept(this); - - switch (unary->token()) { - case TOK_TILDE: - __ bitnot(); - break; - - case TOK_MINUS: - if (unary->type()->isFloat()) - __ neg_f(); - else - __ neg(); - break; - - default: - assert(unary->token() == TOK_LABEL); - __ bitcast(unary->type()); - break; - } -} - -void -HIRTranslator::visitConvert(HConvert *convert) -{ - convert->in()->accept(this); - __ unary(convert->opcode()); -} - -void -HIRTranslator::visitToRef(HToRef *hir) -{ - HIR *in = hir->in(); - - // See if we can bind a direct reference. - if (in->isLValue() && in->toLValue()->ref(emitter_)) - return; - - // No, we can't. So instead we create an on-stack reference. - in->accept(this); - __ slotref_lifo(); -} - -void -HIRTranslator::visitNewDependentArray(HNewDependentArray *hir) -{ - hir->base()->accept(this); - hir->index()->accept(this); - - Local type(zone_, ArrayType::cast(hir->type())); - - // The dependent map is used for the opcode, but is not attached. - Local map(zone_, ArrayMap::NewDependent(zone_, type)); - if (!map) - return; - - __ deparray(map); -} - -void -HIRTranslator::visitImport(HImport *import) -{ - __ import(import->index()); -} - -void -ke::EmitHIR(Zone *zone, BytecodeEmitter &emitter, HIR *hir) -{ - HIRTranslator translator(zone, emitter); - translator.translate(hir); -} +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include "AST.h" +#include "HIR.h" +#include "BytecodeEmitter.h" +#include "../Strings.h" +#include "../Heap-inl.h" +#include "../Handles-inl.h" + +using namespace ke; + +class HIRTranslator : public HIRVisitor +{ + public: + HIRTranslator(Zone *zone, BytecodeEmitter &emitter) + : zone_(zone), + emitter_(emitter) + { + } + + void translate(HIR *hir) { + hir->accept(this); + } + + void visitBoolean(HBoolean *hir); + void visitFloat(HFloat *hir); + void visitInteger(HInteger *hir); + void visitArray(HArray *hir); + void visitCall(HCall *call); + void visitStore(HStore *store); + void visitUnary(HUnary *unary); + void visitBinary(HBinary *binary); + void visitLocal(HLocal *local); + void visitGlobal(HGlobal *global); + void visitConvert(HConvert *convert); + void visitIndex(HIndex *index); + void visitField(HField *field); + void visitPostIncDec(HPostIncDec *hir); + void visitToRef(HToRef *hir); + void visitNewDependentArray(HNewDependentArray *hir); + void visitImport(HImport *import); + + private: + Zone *zone_; + BytecodeEmitter &emitter_; +}; + +#define __ emitter_. + +void +HIRTranslator::visitBoolean(HBoolean *hir) +{ + if (hir->value()) + __ true_(); + else + __ false_(); +} + +void +HIRTranslator::visitFloat(HFloat *hir) +{ + __ float_(hir->value()); +} + +void +HIRTranslator::visitInteger(HInteger *hir) +{ + __ int_(hir->value()); +} + +void +HIRTranslator::visitArray(HArray *hir) +{ + __ object(hir->array()); + + if (hir->duplicate()) + __ duparray(); +} + +void +HIRTranslator::visitCall(HCall *call) +{ + Local fun(zone_, FunctionType::cast(call->callee()->type())); + + assert(call->args()->length() == fun->parameters()->length()); + + __ note_position(call->node()->pos()); + + unsigned argc = call->args()->length(); + for (size_t i = call->args()->length() - 1; i < call->args()->length(); i--) { + HIR *hir = call->args()->at(i); + hir->accept(this); + } + + call->callee()->accept(this); + __ call(fun, argc); +} + +void +HGlobal::bind(HIRVisitor *visitor, BytecodeEmitter &emitter_) +{ +} + +void +HGlobal::hold(BytecodeEmitter &emitter_) +{ +} + +void +HGlobal::swapAndPick(BytecodeEmitter &emitter_) +{ + __ dup(); +} + +void +HGlobal::store(BytecodeEmitter &emitter_) +{ + __ setglobal(sym_); +} + +void +HGlobal::load(BytecodeEmitter &emitter_) +{ + __ getglobal(sym_); +} + +bool +HGlobal::ref(BytecodeEmitter &emitter_) +{ + return false; +} + +static inline Type * +MaybeDereference(Type *type) +{ + if (type->isReference()) + return ReferenceType::cast(type)->contained(); + return type; +} + +HLocal::HLocal(AstNode *node, VariableSymbol *sym) + : HLValue(node, MaybeDereference(sym->type())), + sym_(sym) +{ +} + +void +HLocal::bind(HIRVisitor *visitor, BytecodeEmitter &emitter_) +{ + if (sym_->type()->isReference()) { + if (sym_->storage() == VariableSymbol::Local) + __ getlocal(sym_); + else if (sym_->storage() == VariableSymbol::Arg) + __ getarg(sym_); + else + assert(false); + } +} + +void +HLocal::hold(BytecodeEmitter &emitter_) +{ + if (sym_->type()->isReference()) + __ dup(); +} + +void +HLocal::swapAndPick(BytecodeEmitter &emitter_) +{ + if (sym_->type()->isReference()) { + __ swap(); + __ pick2(); + } else { + __ dup(); + } +} + +void +HLocal::store(BytecodeEmitter &emitter_) +{ + if (sym_->type()->isReference()) { + __ setref(ReferenceType::cast(sym_->type())->contained()); + } else { + if (sym_->storage() == VariableSymbol::Local) + __ setlocal(sym_); + else if (sym_->storage() == VariableSymbol::Arg) + __ setarg(sym_); + else + assert(false); + } +} + +void +HLocal::load(BytecodeEmitter &emitter_) +{ + if (sym_->type()->isReference()) + __ getref(ReferenceType::cast(sym_->type())->contained()); + else if (sym_->storage() == VariableSymbol::Local) + __ getlocal(sym_); + else if (sym_->storage() == VariableSymbol::Arg) + __ getarg(sym_); + else + assert(false); +} + +bool +HLocal::ref(BytecodeEmitter &emitter_) +{ + if (sym_->storage() == VariableSymbol::Heap) { + // Closing over a reference should never pass SemA. + assert(!sym_->type()->isReference()); + + // We don't support direct references to heap-allocated locals. This + // should probably be banned in SemA? But for now, just assert. :TODO: + assert(false); + return false; + } + + if (sym_->type()->isReference()) { + if (sym_->storage() == VariableSymbol::Local) + __ getlocal(sym_); + else if (sym_->storage() == VariableSymbol::Arg) + __ getarg(sym_); + else + assert(false); + } else { + __ newref(sym_); + } + + return true; +} + +void +HIndex::bind(HIRVisitor *visitor, BytecodeEmitter &emitter_) +{ + __ note_position(node()->pos()); + left_->accept(visitor); + right_->accept(visitor); +} + +void +HIndex::hold(BytecodeEmitter &emitter_) +{ + __ dup2(); +} + +void +HIndex::swapAndPick(BytecodeEmitter &emitter_) +{ + // BASE INDEX VALUE + __ roll3(); // INDEX VALUE BASE + __ roll3(); // VALUE BASE INDEX + __ pick3(); // VALUE BASE INDEX VALUE +} + +void +HIndex::store(BytecodeEmitter &emitter_) +{ + __ setelem(type()); +} + +void +HIndex::load(BytecodeEmitter &emitter_) +{ + __ getelem(type()); +} + +bool +HIndex::ref(BytecodeEmitter &emitter_) +{ + return false; +} + +void +HField::bind(HIRVisitor *visitor, BytecodeEmitter &emitter_) +{ + __ note_position(node()->pos()); + base_->accept(visitor); +} + +void +HField::hold(BytecodeEmitter &emitter_) +{ + __ dup(); +} + +void +HField::swapAndPick(BytecodeEmitter &emitter_) +{ + // BASE VALUE + __ swap(); // VALUE BASE + __ pick2(); // VALUE BASE VALUE +} + +void +HField::store(BytecodeEmitter &emitter_) +{ + __ setfield(index_, type()); +} + +void +HField::load(BytecodeEmitter &emitter_) +{ + __ getfield(index_, type()); +} + +bool +HField::ref(BytecodeEmitter &emitter_) +{ + return false; +} + +void +HIRTranslator::visitLocal(HLocal *local) +{ + local->bind(this, emitter_); + local->load(emitter_); +} + +void +HIRTranslator::visitIndex(HIndex *index) +{ + index->bind(this, emitter_); + index->load(emitter_); +} + +void +HIRTranslator::visitField(HField *field) +{ + field->bind(this, emitter_); + field->load(emitter_); +} + +void +HIRTranslator::visitGlobal(HGlobal *global) +{ + global->bind(this, emitter_); + global->load(emitter_); +} + +static inline Opcode +TokenToOpcode(TokenKind kind) +{ + switch (kind) { + case TOK_PLUS: + case TOK_ASSIGN_ADD: + case TOK_INCREMENT: + return OP_ADD; + case TOK_MINUS: + case TOK_ASSIGN_SUB: + case TOK_DECREMENT: + return OP_SUB; + case TOK_STAR: + case TOK_ASSIGN_MUL: + return OP_MUL; + case TOK_SLASH: + case TOK_ASSIGN_DIV: + return OP_DIV; + case TOK_PERCENT: + case TOK_ASSIGN_MOD: + return OP_MOD; + case TOK_SHR: + case TOK_ASSIGN_SHR: + return OP_SHR; + case TOK_USHR: + case TOK_ASSIGN_USHR: + return OP_USHR; + case TOK_SHL: + case TOK_ASSIGN_SHL: + return OP_SHL; + case TOK_BITAND: + case TOK_ASSIGN_BITAND: + return OP_BITAND; + case TOK_BITOR: + case TOK_ASSIGN_BITOR: + return OP_BITOR; + case TOK_GT: + return OP_GT; + case TOK_GE: + return OP_GE; + case TOK_LT: + return OP_LT; + case TOK_LE: + return OP_LE; + case TOK_EQUALS: + return OP_EQ; + case TOK_NOTEQUALS: + return OP_NE; + default: + assert(kind == TOK_BITXOR); + return OP_BITXOR; + } +} + +void +HIRTranslator::visitStore(HStore *store) +{ + store->lval()->bind(this, emitter_); + + if (store->kind() == TOK_ASSIGN) { + // Fixed-length arrays and structs have copy semantics. + Local type(zone_, store->lval()->type()); + if (type->isArray() && ArrayType::cast(type)->isFixedLength()) { + store->lval()->load(emitter_); + store->rval()->accept(this); + __ copyarray(); + } else if (type->isStruct()) { + store->lval()->load(emitter_); + store->rval()->accept(this); + __ copystruct(); + } else { + store->rval()->accept(this); + store->lval()->store(emitter_); + } + } else { + // Advanced case: dup the ref, load, op, store. + store->lval()->hold(emitter_); + store->lval()->load(emitter_); + store->rval()->accept(this); + __ binary(TokenToOpcode(store->kind())); + store->lval()->store(emitter_); + } +} + +void +HIRTranslator::visitPostIncDec(HPostIncDec *hir) +{ + hir->lval()->bind(this, emitter_); + hir->lval()->hold(emitter_); + hir->lval()->load(emitter_); + hir->lval()->swapAndPick(emitter_); + hir->rval()->accept(this); + __ binary(TokenToOpcode(hir->kind())); + hir->lval()->store(emitter_); + __ pop(); +} + +void +HIRTranslator::visitBinary(HBinary *binary) +{ + binary->left()->accept(this); + binary->right()->accept(this); + + // We define three sets of math operators: + // + // (1) ADD, SUB, MUL, DIV, MOD, which take two equal types and produce + // the same type. + // (2) LT, LE, GE, GT, EQ, NE, which take two equal types and produce + // a boolean. + // (3) SHL, SR, USHR, BITAND, BITOR, BITXOR, BITNOT, which take two + // equal integer types and return that integer type. + // + Opcode op = TokenToOpcode(binary->token()); + + if (op == OP_DIV) + __ note_position(binary->node()->pos()); + + if (op >= OP_ADD && op <= OP_NE) { + if (binary->type()->isFloat()) + op = Opcode(int(op) + int(OP_ADD_F - OP_ADD)); + } else { + assert(binary->type()->isInt32()); + } + + __ binary(op); +} + +void +HIRTranslator::visitUnary(HUnary *unary) +{ + unary->in()->accept(this); + + switch (unary->token()) { + case TOK_TILDE: + __ bitnot(); + break; + + case TOK_MINUS: + if (unary->type()->isFloat()) + __ neg_f(); + else + __ neg(); + break; + + default: + assert(unary->token() == TOK_LABEL); + __ bitcast(unary->type()); + break; + } +} + +void +HIRTranslator::visitConvert(HConvert *convert) +{ + convert->in()->accept(this); + __ unary(convert->opcode()); +} + +void +HIRTranslator::visitToRef(HToRef *hir) +{ + HIR *in = hir->in(); + + // See if we can bind a direct reference. + if (in->isLValue() && in->toLValue()->ref(emitter_)) + return; + + // No, we can't. So instead we create an on-stack reference. + in->accept(this); + __ slotref_lifo(); +} + +void +HIRTranslator::visitNewDependentArray(HNewDependentArray *hir) +{ + hir->base()->accept(this); + hir->index()->accept(this); + + Local type(zone_, ArrayType::cast(hir->type())); + + // The dependent map is used for the opcode, but is not attached. + Local map(zone_, ArrayMap::NewDependent(zone_, type)); + if (!map) + return; + + __ deparray(map); +} + +void +HIRTranslator::visitImport(HImport *import) +{ + __ import(import->index()); +} + +void +ke::EmitHIR(Zone *zone, BytecodeEmitter &emitter, HIR *hir) +{ + HIRTranslator translator(zone, emitter); + translator.translate(hir); +} diff -r c4b1341297e5 -r ba85a47ee414 src/compiler/HIR.h --- a/src/compiler/HIR.h Mon Jun 10 00:46:29 2013 +0100 +++ b/src/compiler/HIR.h Mon Jun 10 01:04:43 2013 +0100 @@ -1,607 +1,607 @@ -/* - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#ifndef _include_sp2_hir_h_ -#define _include_sp2_hir_h_ - -#include "../Types.h" -#include "../PoolAllocator.h" -#include "../Opcodes.h" -#include "Symbols.h" - -namespace ke { - -#define HIR_OPS(_) \ - _(Call) \ - _(Boolean) \ - _(Integer) \ - _(Float) \ - _(String) \ - _(Import) \ - _(Array) \ - _(Store) \ - _(ToRef) \ - _(NewDependentArray) \ - /* Special LValue types */ \ - _(Index) \ - _(Local) \ - _(Global) \ - _(Field) \ - /* End LValues */ \ - _(Unary) \ - _(Binary) \ - _(Convert) \ - _(PostIncDec) - -// Forward declarations. -#define _(name) class H##name; -HIR_OPS(_) -#undef _ - -class HIRVisitor; -class AstNode; -class HLValue; -class Array; - -class HIR : public PoolObject -{ - public: - enum HOp { -#define _(name) k##name, - HIR_OPS(_) -#undef _ - kTotalOps - }; - - public: - HIR(AstNode *node, Type *type) - : node_(node), - type_(type) - { - } - - Handle type() { - return type_; - } - AstNode *node() const { - return node_; - } - - virtual bool isLValue() const { - return false; - } - - virtual HOp op() const = 0; - virtual void accept(HIRVisitor *visitor) = 0; - -#define _(name) \ - bool is##name() const { \ - return op() == k##name; \ - } \ - H##name *to##name() { \ - assert(is##name()); \ - return reinterpret_cast(this); \ - } - HIR_OPS(_) -#undef _ - - HLValue *toLValue() { - assert(isLValue()); - return reinterpret_cast(this); - } - - private: - AstNode *node_; - ScopedRoot type_; -}; - -class BytecodeEmitter; - -class HIRVisitor -{ - public: -#define _(name) \ - virtual void visit##name(H##name *hir) { \ - assert(false); \ - } - HIR_OPS(_) -#undef _ -}; - -typedef PoolList HIRList; - -#define DEFINE_HIR(name) \ - virtual HOp op() const { \ - return k##name; \ - } \ - virtual void accept(HIRVisitor *visitor) { \ - visitor->visit##name(this); \ - } - -class HBoolean : public HIR -{ - public: - HBoolean(AstNode *node, Type *type, bool value) - : HIR(node, type), - value_(value) - { - } - - DEFINE_HIR(Boolean); - bool value() const { - return value_; - } - - private: - bool value_; -}; - -class HInteger : public HIR -{ - public: - HInteger(AstNode *node, Type *type, int value) - : HIR(node, type), - value_(value) - { - } - - DEFINE_HIR(Integer); - int value() const { - return value_; - } - - private: - int value_; -}; - -class HString : public HIR -{ - public: - HString(AstNode *node, Type *type, String *string) - : HIR(node, type), - string_(string) - { - } - - DEFINE_HIR(String); - Handle string() const { - return string_; - } - - private: - ScopedRoot string_; -}; - -class HImport : public HIR -{ - public: - HImport(AstNode *node, Type *type, unsigned index) - : HIR(node, type), - index_(index) - { - } - - DEFINE_HIR(Import); - unsigned index() const { - return index_; - } - - private: - unsigned index_; -}; - -class HArray : public HIR -{ - public: - HArray(AstNode *node, Type *type, Array *array, bool duplicate) - : HIR(node, type), - array_(array), - duplicate_(duplicate) - { - } - - DEFINE_HIR(Array); - Handle array() const { - return array_; - } - bool duplicate() const { - return duplicate_; - } - - private: - ScopedRoot array_; - bool duplicate_; -}; - -class HConvert : public HIR -{ - public: - HConvert(AstNode *node, Type *type, Opcode op, HIR *in) - : HIR(node, type), - op_(op), - in_(in) - { - } - - DEFINE_HIR(Convert); - Opcode opcode() const { - return op_; - } - HIR *in() const { - return in_; - } - - private: - Opcode op_; - HIR *in_; -}; - -class HLValue : public HIR -{ - public: - HLValue(AstNode *node, Type *type) - : HIR(node, type) - { - } - - // Pushes a reference to the LValue on the stack. - virtual void bind(HIRVisitor *visitor, BytecodeEmitter &emitter_) = 0; - - // Duplicates a stack reference. - virtual void hold(BytecodeEmitter &emitter_) = 0; - - // Reorders the stack for a post-increment operation: - // REF VAL -> VAL REF VAL - virtual void swapAndPick(BytecodeEmitter &emitter_) = 0; - - // Given a stack layout of the form: - // [-1] value - // [-2..n] ref - // - // Emits a store to the reference. - virtual void store(BytecodeEmitter &emitter_) = 0; - virtual void load(BytecodeEmitter &emitter_) = 0; - - // Try to make a direct reference, if semantics allow it. False if the - // caller must allocate a reference to the stack. - virtual bool ref(BytecodeEmitter &emitter_) = 0; - - bool isLValue() const { - return true; - } -}; - -class HLocal : public HLValue -{ - public: - HLocal(AstNode *node, VariableSymbol *sym); - - DEFINE_HIR(Local); - - // Pushes a reference to the LValue on the stack. - void bind(HIRVisitor *visitor, BytecodeEmitter &emitter_); - void hold(BytecodeEmitter &emitter_); - void swapAndPick(BytecodeEmitter &emitter_); - void store(BytecodeEmitter &emitter_); - void load(BytecodeEmitter &emitter_); - bool ref(BytecodeEmitter &emitter_); - - VariableSymbol *sym() const { - return sym_; - } - - private: - VariableSymbol *sym_; -}; - -class HIndex : public HLValue -{ - public: - HIndex(AstNode *node, Type *type, HIR *left, HIR *right) - : HLValue(node, type), - left_(left), - right_(right) - { - } - - DEFINE_HIR(Index); - - void bind(HIRVisitor *visitor, BytecodeEmitter &emitter_); - void hold(BytecodeEmitter &emitter_); - void swapAndPick(BytecodeEmitter &emitter_); - void store(BytecodeEmitter &emitter_); - void load(BytecodeEmitter &emitter_); - bool ref(BytecodeEmitter &emitter_); - - HIR *base() const { - return left_; - } - HIR *index() const { - return right_; - } - - private: - HIR *left_; - HIR *right_; -}; - -class HField : public HLValue -{ - public: - HField(AstNode *node, HIR *left, Type *type, unsigned index) - : HLValue(node, type), - base_(left), - index_(index) - { - } - - DEFINE_HIR(Field); - - void bind(HIRVisitor *visitor, BytecodeEmitter &emitter_); - void hold(BytecodeEmitter &emitter_); - void swapAndPick(BytecodeEmitter &emitter_); - void store(BytecodeEmitter &emitter_); - void load(BytecodeEmitter &emitter_); - bool ref(BytecodeEmitter &emitter_); - - HIR *base() const { - return base_; - } - unsigned index() const { - return index_; - } - - private: - HIR *base_; - unsigned index_; -}; - -class HGlobal : public HLValue -{ - public: - HGlobal(AstNode *node, Symbol *sym) - : HLValue(node, sym->type()), - sym_(sym) - { - } - - DEFINE_HIR(Global); - - void bind(HIRVisitor *visitor, BytecodeEmitter &emitter_); - void hold(BytecodeEmitter &emitter_); - void swapAndPick(BytecodeEmitter &emitter_); - void store(BytecodeEmitter &emitter_); - void load(BytecodeEmitter &emitter_); - bool ref(BytecodeEmitter &emitter_); - - Symbol *sym() const { - return sym_; - } - - private: - Symbol *sym_; -}; - -class HBinary : public HIR -{ - public: - HBinary(AstNode *node, Type *type, TokenKind token, HIR *left, HIR *right) - : HIR(node, type), - token_(token), - left_(left), - right_(right) - { - } - - DEFINE_HIR(Binary); - TokenKind token() const { - return token_; - } - HIR *left() const { - return left_; - } - HIR *right() const { - return right_; - } - - private: - TokenKind token_; - HIR *left_; - HIR *right_; -}; - -class HFloat : public HIR -{ - public: - HFloat(AstNode *node, Type *type, float value) - : HIR(node, type), - value_(value) - { - } - - DEFINE_HIR(Float); - float value() const { - return value_; - } - - private: - float value_; -}; - -class HUnary : public HIR -{ - public: - HUnary(AstNode *node, Type *type, TokenKind tok, HIR *in) - : HIR(node, type), - in_(in), - token_(tok) - { - } - - DEFINE_HIR(Unary); - HIR *in() const { - return in_; - } - TokenKind token() const { - return token_; - } - - private: - HIR *in_; - TokenKind token_; -}; - -class HStore : public HIR -{ - public: - HStore(AstNode *node, HLValue *lval, HIR *rval) - : HIR(node, lval->type()), - kind_(TOK_ASSIGN), - lval_(lval), - rval_(rval) - { - } - - HStore(AstNode *node, TokenKind kind, HLValue *lval, HIR *rval) - : HIR(node, lval->type()), - kind_(kind), - lval_(lval), - rval_(rval) - { - } - - DEFINE_HIR(Store); - - HLValue *lval() const { - return lval_; - } - HIR *rval() const { - return rval_; - } - TokenKind kind() const { - return kind_; - } - - private: - TokenKind kind_; - HLValue *lval_; - HIR *rval_; -}; - -class HPostIncDec : public HIR -{ - public: - HPostIncDec(AstNode *node, TokenKind kind, HLValue *lval, HIR *rval) - : HIR(node, lval->type()), - kind_(kind), - lval_(lval), - rval_(rval) - { - } - - DEFINE_HIR(PostIncDec); - - HLValue *lval() const { - return lval_; - } - HIR *rval() const { - return rval_; - } - TokenKind kind() const { - return kind_; - } - - private: - TokenKind kind_; - HLValue *lval_; - HIR *rval_; -}; - -class HCall : public HIR -{ - public: - HCall(AstNode *node, Type *type, HIR *callee, HIRList *args) - : HIR(node, type), - callee_(callee), - args_(args) - { - } - - DEFINE_HIR(Call); - - HIR *callee() const { - return callee_; - } - HIRList *args() const { - return args_; - } - - private: - HIR *callee_; - HIRList *args_; -}; - -class HToRef : public HIR -{ - public: - HToRef(AstNode *node, Type *type, HIR *in) - : HIR(node, type), - in_(in) - { - } - - DEFINE_HIR(ToRef); - - HIR *in() const { - return in_; - } - - private: - HIR *in_; -}; - -class HNewDependentArray : public HIR -{ - public: - HNewDependentArray(AstNode *node, Type *type, HIR *base, HIR *index) - : HIR(node, type), - base_(base), - index_(index) - { - } - - DEFINE_HIR(NewDependentArray); - - HIR *base() const { - return base_; - } - HIR *index() const { - return index_; - } - - private: - HIR *base_; - HIR *index_; -}; - -#undef DEFINE_HIR - -void EmitHIR(Zone *zone, BytecodeEmitter &emitter, HIR *hir); - -} - -#endif // _include_sp2_hir_h_ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_sp2_hir_h_ +#define _include_sp2_hir_h_ + +#include "../Types.h" +#include "../PoolAllocator.h" +#include "../Opcodes.h" +#include "Symbols.h" + +namespace ke { + +#define HIR_OPS(_) \ + _(Call) \ + _(Boolean) \ + _(Integer) \ + _(Float) \ + _(String) \ + _(Import) \ + _(Array) \ + _(Store) \ + _(ToRef) \ + _(NewDependentArray) \ + /* Special LValue types */ \ + _(Index) \ + _(Local) \ + _(Global) \ + _(Field) \ + /* End LValues */ \ + _(Unary) \ + _(Binary) \ + _(Convert) \ + _(PostIncDec) + +// Forward declarations. +#define _(name) class H##name; +HIR_OPS(_) +#undef _ + +class HIRVisitor; +class AstNode; +class HLValue; +class Array; + +class HIR : public PoolObject +{ + public: + enum HOp { +#define _(name) k##name, + HIR_OPS(_) +#undef _ + kTotalOps + }; + + public: + HIR(AstNode *node, Type *type) + : node_(node), + type_(type) + { + } + + Handle type() { + return type_; + } + AstNode *node() const { + return node_; + } + + virtual bool isLValue() const { + return false; + } + + virtual HOp op() const = 0; + virtual void accept(HIRVisitor *visitor) = 0; + +#define _(name) \ + bool is##name() const { \ + return op() == k##name; \ + } \ + H##name *to##name() { \ + assert(is##name()); \ + return reinterpret_cast(this); \ + } + HIR_OPS(_) +#undef _ + + HLValue *toLValue() { + assert(isLValue()); + return reinterpret_cast(this); + } + + private: + AstNode *node_; + ScopedRoot type_; +}; + +class BytecodeEmitter; + +class HIRVisitor +{ + public: +#define _(name) \ + virtual void visit##name(H##name *hir) { \ + assert(false); \ + } + HIR_OPS(_) +#undef _ +}; + +typedef PoolList HIRList; + +#define DEFINE_HIR(name) \ + virtual HOp op() const { \ + return k##name; \ + } \ + virtual void accept(HIRVisitor *visitor) { \ + visitor->visit##name(this); \ + } + +class HBoolean : public HIR +{ + public: + HBoolean(AstNode *node, Type *type, bool value) + : HIR(node, type), + value_(value) + { + } + + DEFINE_HIR(Boolean); + bool value() const { + return value_; + } + + private: + bool value_; +}; + +class HInteger : public HIR +{ + public: + HInteger(AstNode *node, Type *type, int value) + : HIR(node, type), + value_(value) + { + } + + DEFINE_HIR(Integer); + int value() const { + return value_; + } + + private: + int value_; +}; + +class HString : public HIR +{ + public: + HString(AstNode *node, Type *type, String *string) + : HIR(node, type), + string_(string) + { + } + + DEFINE_HIR(String); + Handle string() const { + return string_; + } + + private: + ScopedRoot string_; +}; + +class HImport : public HIR +{ + public: + HImport(AstNode *node, Type *type, unsigned index) + : HIR(node, type), + index_(index) + { + } + + DEFINE_HIR(Import); + unsigned index() const { + return index_; + } + + private: + unsigned index_; +}; + +class HArray : public HIR +{ + public: + HArray(AstNode *node, Type *type, Array *array, bool duplicate) + : HIR(node, type), + array_(array), + duplicate_(duplicate) + { + } + + DEFINE_HIR(Array); + Handle array() const { + return array_; + } + bool duplicate() const { + return duplicate_; + } + + private: + ScopedRoot array_; + bool duplicate_; +}; + +class HConvert : public HIR +{ + public: + HConvert(AstNode *node, Type *type, Opcode op, HIR *in) + : HIR(node, type), + op_(op), + in_(in) + { + } + + DEFINE_HIR(Convert); + Opcode opcode() const { + return op_; + } + HIR *in() const { + return in_; + } + + private: + Opcode op_; + HIR *in_; +}; + +class HLValue : public HIR +{ + public: + HLValue(AstNode *node, Type *type) + : HIR(node, type) + { + } + + // Pushes a reference to the LValue on the stack. + virtual void bind(HIRVisitor *visitor, BytecodeEmitter &emitter_) = 0; + + // Duplicates a stack reference. + virtual void hold(BytecodeEmitter &emitter_) = 0; + + // Reorders the stack for a post-increment operation: + // REF VAL -> VAL REF VAL + virtual void swapAndPick(BytecodeEmitter &emitter_) = 0; + + // Given a stack layout of the form: + // [-1] value + // [-2..n] ref + // + // Emits a store to the reference. + virtual void store(BytecodeEmitter &emitter_) = 0; + virtual void load(BytecodeEmitter &emitter_) = 0; + + // Try to make a direct reference, if semantics allow it. False if the + // caller must allocate a reference to the stack. + virtual bool ref(BytecodeEmitter &emitter_) = 0; + + bool isLValue() const { + return true; + } +}; + +class HLocal : public HLValue +{ + public: + HLocal(AstNode *node, VariableSymbol *sym); + + DEFINE_HIR(Local); + + // Pushes a reference to the LValue on the stack. + void bind(HIRVisitor *visitor, BytecodeEmitter &emitter_); + void hold(BytecodeEmitter &emitter_); + void swapAndPick(BytecodeEmitter &emitter_); + void store(BytecodeEmitter &emitter_); + void load(BytecodeEmitter &emitter_); + bool ref(BytecodeEmitter &emitter_); + + VariableSymbol *sym() const { + return sym_; + } + + private: + VariableSymbol *sym_; +}; + +class HIndex : public HLValue +{ + public: + HIndex(AstNode *node, Type *type, HIR *left, HIR *right) + : HLValue(node, type), + left_(left), + right_(right) + { + } + + DEFINE_HIR(Index); + + void bind(HIRVisitor *visitor, BytecodeEmitter &emitter_); + void hold(BytecodeEmitter &emitter_); + void swapAndPick(BytecodeEmitter &emitter_); + void store(BytecodeEmitter &emitter_); + void load(BytecodeEmitter &emitter_); + bool ref(BytecodeEmitter &emitter_); + + HIR *base() const { + return left_; + } + HIR *index() const { + return right_; + } + + private: + HIR *left_; + HIR *right_; +}; + +class HField : public HLValue +{ + public: + HField(AstNode *node, HIR *left, Type *type, unsigned index) + : HLValue(node, type), + base_(left), + index_(index) + { + } + + DEFINE_HIR(Field); + + void bind(HIRVisitor *visitor, BytecodeEmitter &emitter_); + void hold(BytecodeEmitter &emitter_); + void swapAndPick(BytecodeEmitter &emitter_); + void store(BytecodeEmitter &emitter_); + void load(BytecodeEmitter &emitter_); + bool ref(BytecodeEmitter &emitter_); + + HIR *base() const { + return base_; + } + unsigned index() const { + return index_; + } + + private: + HIR *base_; + unsigned index_; +}; + +class HGlobal : public HLValue +{ + public: + HGlobal(AstNode *node, Symbol *sym) + : HLValue(node, sym->type()), + sym_(sym) + { + } + + DEFINE_HIR(Global); + + void bind(HIRVisitor *visitor, BytecodeEmitter &emitter_); + void hold(BytecodeEmitter &emitter_); + void swapAndPick(BytecodeEmitter &emitter_); + void store(BytecodeEmitter &emitter_); + void load(BytecodeEmitter &emitter_); + bool ref(BytecodeEmitter &emitter_); + + Symbol *sym() const { + return sym_; + } + + private: + Symbol *sym_; +}; + +class HBinary : public HIR +{ + public: + HBinary(AstNode *node, Type *type, TokenKind token, HIR *left, HIR *right) + : HIR(node, type), + token_(token), + left_(left), + right_(right) + { + } + + DEFINE_HIR(Binary); + TokenKind token() const { + return token_; + } + HIR *left() const { + return left_; + } + HIR *right() const { + return right_; + } + + private: + TokenKind token_; + HIR *left_; + HIR *right_; +}; + +class HFloat : public HIR +{ + public: + HFloat(AstNode *node, Type *type, float value) + : HIR(node, type), + value_(value) + { + } + + DEFINE_HIR(Float); + float value() const { + return value_; + } + + private: + float value_; +}; + +class HUnary : public HIR +{ + public: + HUnary(AstNode *node, Type *type, TokenKind tok, HIR *in) + : HIR(node, type), + in_(in), + token_(tok) + { + } + + DEFINE_HIR(Unary); + HIR *in() const { + return in_; + } + TokenKind token() const { + return token_; + } + + private: + HIR *in_; + TokenKind token_; +}; + +class HStore : public HIR +{ + public: + HStore(AstNode *node, HLValue *lval, HIR *rval) + : HIR(node, lval->type()), + kind_(TOK_ASSIGN), + lval_(lval), + rval_(rval) + { + } + + HStore(AstNode *node, TokenKind kind, HLValue *lval, HIR *rval) + : HIR(node, lval->type()), + kind_(kind), + lval_(lval), + rval_(rval) + { + } + + DEFINE_HIR(Store); + + HLValue *lval() const { + return lval_; + } + HIR *rval() const { + return rval_; + } + TokenKind kind() const { + return kind_; + } + + private: + TokenKind kind_; + HLValue *lval_; + HIR *rval_; +}; + +class HPostIncDec : public HIR +{ + public: + HPostIncDec(AstNode *node, TokenKind kind, HLValue *lval, HIR *rval) + : HIR(node, lval->type()), + kind_(kind), + lval_(lval), + rval_(rval) + { + } + + DEFINE_HIR(PostIncDec); + + HLValue *lval() const { + return lval_; + } + HIR *rval() const { + return rval_; + } + TokenKind kind() const { + return kind_; + } + + private: + TokenKind kind_; + HLValue *lval_; + HIR *rval_; +}; + +class HCall : public HIR +{ + public: + HCall(AstNode *node, Type *type, HIR *callee, HIRList *args) + : HIR(node, type), + callee_(callee), + args_(args) + { + } + + DEFINE_HIR(Call); + + HIR *callee() const { + return callee_; + } + HIRList *args() const { + return args_; + } + + private: + HIR *callee_; + HIRList *args_; +}; + +class HToRef : public HIR +{ + public: + HToRef(AstNode *node, Type *type, HIR *in) + : HIR(node, type), + in_(in) + { + } + + DEFINE_HIR(ToRef); + + HIR *in() const { + return in_; + } + + private: + HIR *in_; +}; + +class HNewDependentArray : public HIR +{ + public: + HNewDependentArray(AstNode *node, Type *type, HIR *base, HIR *index) + : HIR(node, type), + base_(base), + index_(index) + { + } + + DEFINE_HIR(NewDependentArray); + + HIR *base() const { + return base_; + } + HIR *index() const { + return index_; + } + + private: + HIR *base_; + HIR *index_; +}; + +#undef DEFINE_HIR + +void EmitHIR(Zone *zone, BytecodeEmitter &emitter, HIR *hir); + +} + +#endif // _include_sp2_hir_h_ diff -r c4b1341297e5 -r ba85a47ee414 src/compiler/NameBinding.cpp --- a/src/compiler/NameBinding.cpp Mon Jun 10 00:46:29 2013 +0100 +++ b/src/compiler/NameBinding.cpp Mon Jun 10 01:04:43 2013 +0100 @@ -1,1297 +1,1297 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#include "../Strings.h" -#include "../Zone.h" -#include "../TypeManager.h" -#include "NameBinding.h" -#include "Scopes.h" -#include "ConstantEvaluator.h" -#include "../Spaces-inl.h" -#include "../Heap-inl.h" - -using namespace ke; - -// Create symbol tables and symbols for every scope and declaration. For type -// declarations and methods, we also pre-allocate a Type object, so that -// circular dependencies between types will work. -class NamePopulator : public AstVisitor -{ - private: - // This class is analagous to a Scope, however, it allows us to create - // scopes lazily. If a scope is requested, and the current environment - // has no scope, then a scope is created. If no scope is ever requested, - // a scope does not need to be created. - class SymbolEnv - { - public: - SymbolEnv(SymbolEnv **prevp, Scope *scope = NULL) - : envp_(prevp), - prev_(*prevp), - scope_(scope) - { - *envp_ = this; - } - ~SymbolEnv() { - *envp_ = prev_; - } - - Scope *scope() { - return scope_; - } - void setScope(Scope *scope) { - assert(!scope_); - scope_ = scope; - } - SymbolEnv *prev() const { - return prev_; - } - - private: - SymbolEnv **envp_; - SymbolEnv *prev_; - Scope *scope_; - }; - - enum ReturnStatus { - NoReturn, - VoidReturn, - ValueReturn - }; - - class FunctionScope - { - public: - FunctionScope(FunctionScope **prevp) - : prevp_(prevp), - prev_(*prevp), - status_(NoReturn) - { - *prevp_ = this; - } - ~FunctionScope() { - *prevp_ = prev_; - } - ReturnStatus returns() const { - return status_; - } - void returns(ReturnStatus status) { - if (status > status_) - status_ = status; - } - private: - FunctionScope **prevp_; - FunctionScope *prev_; - ReturnStatus status_; - }; - - public: - NamePopulator(Zone *zone, CompileContext &cc, TranslationUnit *unit) - : zone_(zone), - pool_(zone->pool()), - cc_(cc), - unit_(unit), - env_(NULL), - fun_(NULL) - { - } - - void analyze() { - ImportScope *importScope = ImportScope::New(pool_); - if (!importScope) - return; - - // At the import level, we declare system types. - if (!declareSystemTypes(importScope)) - return; - - GlobalScope *globalScope = GlobalScope::New(pool_, importScope); - if (!globalScope) - return; - - unit_->setImportScope(importScope); - unit_->setGlobalScope(globalScope); - - SymbolEnv env(&env_, globalScope); - - for (size_t i = 0; i < unit_->tree()->statements()->length(); i++) { - Statement *stmt = unit_->tree()->statements()->at(i); - stmt->accept(this); - } - - return; - } - - bool declareSystemType(ImportScope *scope, const char *name, Handle effective) { - Local tag(zone_, zone_->makeSymbol(name)); - if (!tag) - return false; - - Local type(zone_, zone_->types()->getType()); - - TypeSymbol *sym = new (pool_) TypeSymbol(scope, tag, type, effective); - return scope->addSymbol(sym); - } - - bool declareSystemType(ImportScope *scope, const char *name, PrimitiveType prim) { - Local primitive(zone_, zone_->types()->getPrimitive(prim)); - assert(primitive); - return declareSystemType(scope, name, primitive); - } - - bool declareSystemTypes(ImportScope *scope) { - if (!declareSystemType(scope, "float", PrimitiveType_Float)) - return false; - if (!declareSystemType(scope, "_", PrimitiveType_Int32)) - return false; - if (!declareSystemType(scope, "int", PrimitiveType_Int32)) - return false; - if (!declareSystemType(scope, "bool", PrimitiveType_Bool)) - return false; - if (!declareSystemType(scope, "char", PrimitiveType_Char)) - return false; - - Local voidType(zone_, zone_->types()->getVoid()); - if (!declareSystemType(scope, "void", voidType)) - return false; - - return true; - } - - void visitVariableDeclaration(VariableDeclaration *node) { - Scope *scope = env_->scope(); - if (!scope || scope->kind() != Scope::GLOBAL) { - if ((scope = getOrCreateBlockScope()) == NULL) - return; - } - - if (scope->kind() == Scope::GLOBAL) { - // The symbol must be added now, since it could be an export. - VariableSymbol *sym = new (pool_) VariableSymbol(scope, node->name(), node->pos()); - node->setSymbol(sym); - - if (!registerName(scope, sym)) - return; - } else { - // We ensure the scope will exist at the name binding phase, but - // we don't actually register the variable symbol yet. Otherwise, - // when binding the initializer, we could encounter: - // new x = x; - // - // And we don't necessarily want this to bind. - } - - if (node->initialization()) - node->initialization()->accept(this); - } - - void visitEnumStatement(EnumStatement *node) { - Local name(zone_, node->name()); - Local type(zone_); - if (name) - type = zone_->types()->newEnum(name); - else - type = zone_->types()->getPrimitive(PrimitiveType_Int32); - - if (!type) - return; - - Scope *scope = getGlobalOrCreateBlockScope(); - if (!scope) - return; - - if (name) { - Local symType(zone_, zone_->types()->getType()); - TypeSymbol *sym = new (pool_) TypeSymbol(scope, name, symType, type); - if (!registerName(scope, sym)) - return; - - if (scope->kind() == Scope::GLOBAL) { - if (!unit_->globalScope()->exported()->append(sym)) - return; - } - } - - // We want each field of the expression to qualify as a constant expression. - if ((type = zone_->types()->qualify(type, TypeQual_ConstExpr)) == NULL) - return; - - int value = 0; - for (size_t i = 0; i < node->entries()->length(); i++) { - EnumStatement::Entry &entry = node->entries()->at(i); - BoxedPrimitive box = BoxedPrimitive::Int(value); - if (entry.expr) { - if (!EvaluateForConstant(entry.expr, &box) || box.type() != PrimitiveType_Int32) { - cc_.reportError(entry.expr->pos(), Message_EnumFieldNotConstant); - return; - } - - assert(box.type() == PrimitiveType_Int32); - value = box.toInt(); - } - - ConstantSymbol *sym = new (pool_) ConstantSymbol(scope, entry.name, entry.pos, type, box); - if (!registerName(scope, sym)) - return; - if (scope->kind() == Scope::GLOBAL) { - if (!unit_->globalScope()->exported()->append(sym)) - return; - } - - entry.sym = sym; - value++; - } - } - - void visitFunctionStatement(FunctionStatement *node) { - // We create function types ahead of time, not for any particular - // reason. It makes things consistent with FunctionTypeStatement, - // which actually does produce a type. - Local type(zone_, FunctionType::New(zone_, node->name(), node->token())); - if (!type) - return; - - assert(env_->scope()); - - // Function statements are always named, so add the name to the outer scope. - FunctionSymbol *sym = new (pool_) FunctionSymbol(env_->scope(), node->name(), node->pos(), type); - if (!registerName(env_->scope(), sym)) - return; - - if (node->token() == TOK_PUBLIC || - node->token() == TOK_FORWARD || - node->token() == TOK_NATIVE) - { - // We're in the global scope, for sure. - assert(env_->scope()->kind() == Scope::GLOBAL); - - if (!unit_->globalScope()->exported()->append(sym)) - return; - } - - node->setSymbol(sym); - - // Scope for the function's arguments. - SymbolEnv argEnv(&env_); - if (!registerArguments(node->signature())) - return; - - // Scope for function body. - SymbolEnv localEnv(&env_); - FunctionScope fun(&fun_); - if (node->body()) - node->body()->accept(this); - - // Function statements have quirky semantics around their return - // type. For reasonable compatibility with SP1, we use the following - // heuristics for when no explicit type is declared. - if (!node->signature().returnType()) { - Local type(zone_); - if (node->token() == TOK_FORWARD || node->token() == TOK_NATIVE) { - // If the function type is a native or forward, we always - // assume it returns an int32. - type = zone_->types()->getPrimitive(PrimitiveType_Int32); - } else if (fun.returns() != ValueReturn) { - // This function never returns a value, so mark it as - // returning void. - type = zone_->types()->getVoid(); - } - // No need to NULL check here, we'll catch OOM later. - node->sym()->type()->setReturnType(type); - } - - node->setScopes(argEnv.scope(), localEnv.scope()); - } - - void visitStructureStatement(StructureStatement *node) { - assert(false); - } - - void visiFunctionTypeStatementt(FunctionTypeStatement *node) { - assert(false); - } - - void visitNameProxy(NameProxy *name) { - } - void visitAssignment(Assignment *node) { - node->lvalue()->accept(this); - node->expression()->accept(this); - } - void visitBinaryExpression(BinaryExpression *node) { - node->left()->accept(this); - node->right()->accept(this); - } - void visitReturnStatement(ReturnStatement *node) { - if (node->expression()) - fun_->returns(ValueReturn); - else - fun_->returns(VoidReturn); - } - void visitForStatement(ForStatement *node) { - SymbolEnv env(&env_); - - if (node->initialization()) - node->initialization()->accept(this); - if (node->condition()) - node->condition()->accept(this); - if (node->update()) - node->update()->accept(this); - node->body()->accept(this); - - node->setScope(env.scope()); - } - void visitBlockStatement(BlockStatement *node) { - if (node->type() == TOK_NEW) { - // Don't create a symbol environment for this block, since it - // was created solely to house multiple variable decls. - for (size_t i = 0; i < node->statements()->length(); i++) - node->statements()->at(i)->accept(this); - } else { - assert(node->type() == TOK_LBRACE); - - SymbolEnv env(&env_); - - for (size_t i = 0; i < node->statements()->length(); i++) - node->statements()->at(i)->accept(this); - - if (node->type() == TOK_NEW) { - assert(!env.scope()); - return; - } - - node->setScope(env.scope()); - } - } - void visitIntegerLiteral(IntegerLiteral *node) { - } - void visitExpressionStatement(ExpressionStatement *node) { - node->expression()->accept(this); - } - void visitCallExpression(CallExpression *node) { - node->callee()->accept(this); - for (size_t i = 0; i < node->arguments()->length(); i++) - node->arguments()->at(i)->accept(this); - } - void visitIfStatement(IfStatement *node) { - node->ifTrue()->accept(this); - if (node->ifFalse()) - node->ifFalse()->accept(this); - } - void visitIndexExpression(IndexExpression *node) { - node->left()->accept(this); - node->right()->accept(this); - } - void visitFloatLiteral(FloatLiteral *node) { - } - void visitWhileStatement(WhileStatement *node) { - node->condition()->accept(this); - node->body()->accept(this); - } - void visitBreakStatement(BreakStatement *node) { - } - void visitContinueStatement(ContinueStatement *node) { - } - void visitStringLiteral(StringLiteral *node) { - } - void visitIncDecExpression(IncDecExpression *node) { - node->expression()->accept(this); - } - void visitUnaryExpression(UnaryExpression *node) { - node->expression()->accept(this); - } - void visitTernaryExpression(TernaryExpression *node) { - assert(false); - } - void visitBooleanLiteral(BooleanLiteral *node) { - } - void visitFieldExpression(FieldExpression *node) { - } - void visitSwitchStatement(SwitchStatement *node) { - if (node->defaultCase()) - node->defaultCase()->accept(this); - - for (size_t i = 0; i < node->cases()->length(); i++) - node->cases()->at(i)->statement()->accept(this); - } - void visitArrayLiteral(ArrayLiteral *node) { - } - void visitImportStatement(ImportStatement *node) { - } - - private: - Scope *getGlobalOrCreateBlockScope() { - if (env_->scope()) { - assert(env_->scope()->kind() == Scope::BLOCK || - env_->scope()->kind() == Scope::GLOBAL); - return env_->scope(); - } - return getOrCreateBlockScope(); - } - Scope *getOrCreateBlockScope() { - if (env_->scope()) { - assert(env_->scope()->kind() == Scope::BLOCK); - return env_->scope(); - } - - LocalScope *scope = LocalScope::New(pool_); - if (!scope) - return NULL; - - env_->setScope(scope); - return scope; - } - - bool registerName(Scope *scope, Symbol *sym) { - if (!scope) - return false; - - if (Symbol *other = scope->localLookup(sym->name())) { - // Report, but allow errors to continue. - cc_.reportError(sym->pos(), Message_RedeclaredName, - sym->name()->chars(), - other->pos().line, - other->pos().col); - return true; - } - - return scope->addSymbol(sym); - } - - bool registerArguments(const FunctionSignature &sig) { - if (!sig.parameters()->length()) - return true; - - ArgumentScope *argScope = ArgumentScope::New(pool_); - if (!argScope) - return false; - env_->setScope(argScope); - - for (size_t i = 0; i < sig.parameters()->length(); i++) { - Parameter *param = sig.parameters()->at(i); - VariableSymbol *sym = new (pool_) VariableSymbol(argScope, param->name(), param->pos()); - if (!registerName(env_->scope(), sym)) - return false; - param->setSymbol(sym); - } - - return true; - } - - private: - Zone *zone_; - PoolAllocator &pool_; - CompileContext &cc_; - TranslationUnit *unit_; - SymbolEnv *env_; - FunctionScope *fun_; -}; - -void -ke::PopulateNamesAndTypes(Zone *zone, CompileContext &cc, TranslationUnit *unit) -{ - AutoEnterCompileContext enter(cc, unit); - NamePopulator populator(zone, cc, unit); - populator.analyze(); -} - -static const int INFER_ARRAY_SIZE = 0; -static const int EVAL_ARRAY_SIZE = -1; -static const int DYNAMIC_ARRAY_SIZE = -2; - -class NameBinderBase : public AstVisitor -{ - public: - NameBinderBase(Zone *zone, CompileContext &cc, TranslationUnit *unit) - : zone_(zone), - pool_(zone->pool()), - cc_(cc), - unit_(unit) - { - } - - protected: - Type *buildVariableType(VariableDeclaration *node) { - Local type(zone_, bindType(node->type(), node->quals())); - if (!type) - return NULL; - - // We don't allow array typedefs yet. - assert(!type->isArray()); - - if (node->dims()) { - int dims[MAX_ARRAY_DEPTH]; - int levels = evaluateDimensions(node->pos(), type, node->dims(), dims); - if (levels < 0) - return NULL; - - // Do some extra inference based on the initializer, if present. - Expression *init = node->initialization(); - if (init) { - if (init->isArrayLiteral() && init->toArrayLiteral()->isFixed()) { - Local contained(zone_, type->isArray() - ? ArrayType::cast(type)->contained() - : type); - if (!inferFixedArrayDimensions(init->toArrayLiteral(), contained, dims, levels)) - return NULL; - } else if (init->isStringLiteral()) { - dims[levels - 1] = init->toStringLiteral()->string()->length() + 1; - } - } - - // If we got extra dimensions, we need to build a new type. - if (node->dims()) { - if ((type = buildArrayType(type, node->quals(), dims, node->dims()->length())) == NULL) - return NULL; - } - } - - return type; - } - - int evaluateDimensions(const SourcePosition &pos, Handle typeArg, ExpressionList *exprs, int dims[MAX_ARRAY_DEPTH]) { - unsigned level = 0; - Local type(zone_, typeArg); - while (type->isArray()) { - ArrayType *atype = ArrayType::cast(type); - dims[level] = atype->isFixedLength() - ? atype->fixedLength() - : DYNAMIC_ARRAY_SIZE; - type = atype->contained(); - level++; - } - - if (!exprs) - return level; - - if (level + exprs->length() > MAX_ARRAY_DEPTH) { - cc_.reportError(pos, Message_MaximumArrayDepth); - return -1; - } - - bool hadEmptySize = false; - for (size_t i = 0; i < exprs->length(); i++) { - Expression *expr = exprs->at(i); - if (!expr) { - dims[i] = INFER_ARRAY_SIZE; - hadEmptySize = true; - continue; - } - - BoxedPrimitive box; - if (EvaluateForConstant(expr, &box) && box.type() == PrimitiveType_Int32) { - int size = box.toInt(); - if (size <= 0) { - cc_.reportError(expr->pos(), Message_BadArraySize); - return false; - } - - dims[i] = size; - continue; - } - - if (hadEmptySize) { - cc_.reportError(expr->pos(), Message_BadDynamicInitializer); - return false; - } - - dims[i] = EVAL_ARRAY_SIZE; - } - - return level + exprs->length(); - } - - // This function computes missing dimension sizes based on a literal - // initializer. We don't really handle any size rules here; we just - // do a really dumb inference. - bool inferFixedArrayDimensions(ArrayLiteral *lit, Handle contained, int dims[MAX_ARRAY_DEPTH], int levels) { - for (int i = 0; i < levels; i++) { - if (dims[i] == EVAL_ARRAY_SIZE || dims[i] == DYNAMIC_ARRAY_SIZE) { - // The following are invalid: - // new x[] = {...} - // new Type:x = {...} // where Type is a non-fixed array type - cc_.reportError(lit->pos(), Message_CannotInitializeEvalArray); - return false; - } - - if (dims[i] == INFER_ARRAY_SIZE) { - // Character arrays are always dynamic when inferred from string literals. - if (i == levels - 1 && contained->isChar()) - dims[i] = DYNAMIC_ARRAY_SIZE; - else - dims[i] = lit->expressions()->length(); - } - - if (i != levels - 1) { - // Fixed array literals are guaranteed to have at least one expression. - Expression *next = lit->expressions()->at(0); - if (!next->isArrayLiteral()) { - // Whelp... this array is just malformed. Not the best error message - // but whatever. - cc_.reportError(next->pos(), Message_ArraySizeCannotBeDetermined); - return false; - } - lit = next->toArrayLiteral(); - } - } - - return true; - } - - ArrayType *buildArrayType(Handle contained, TypeQualifiers quals, int dims[MAX_ARRAY_DEPTH], size_t postlevels) { - Local type(zone_, contained); - - for (size_t i = postlevels - 1; i < postlevels; i--) { - int size = dims[i] > 0 - ? dims[i] - : ArrayType::DYNAMIC_ARRAY_SIZE; - - if ((type = zone_->types()->newArray(contained, size)) == NULL) - return NULL; - if ((type = zone_->types()->qualify(type, quals)) == NULL) - return NULL; - } - return ArrayType::cast(type); - } - - bool fillFunctionType(Handle fun, const FunctionSignature &sig) { - Local type(zone_); - if (!fun->returnType()) { - if ((type = bindType(sig.returnType(), TypeQual_None)) == NULL) - return false; - fun->setReturnType(type); - } - - if (sig.variadic()) - fun->setVariadic(); - - Local params(zone_, FixedArray::New(zone_, sig.parameters()->length(), Heap::Tenure_Old)); - if (!params) - return false; - - for (size_t i = 0; i < sig.parameters()->length(); i++) { - Parameter *param = sig.parameters()->at(i); - if ((type = bindType(param->type(), param->quals())) == NULL) - return false; - - assert(!type->isArray()); - - if (param->dims()) { - int dims[MAX_ARRAY_DEPTH]; - int levels = evaluateDimensions(param->pos(), type, param->dims(), dims); - if (levels < 0) - return false; - - // If we got extra dimensions, we need to build a new type. - // Unlike variables, we do not infer anything about array - // sizes from its default initializer. - if (param->dims()) { - if ((type = buildArrayType(type, param->quals(), dims, param->dims()->length())) == NULL) - return false; - } - - if (ArrayType::cast(type)->innermost()->isVoid()) { - cc_.reportError(param->type()->pos(), Message_CannotDeclareVoid); - return false; - } - } - - if (param->reference()) { - if (!type->isPod()) { - cc_.reportError(param->pos(), Message_ArrayCantBeReference); - return false; - } - if (param->quals() & TypeQual_Const) { - cc_.reportError(param->pos(), Message_CannotConstReference); - return false; - } - if ((type = zone_->types()->getReference(type, false)) == NULL) - return false; - } - if (param->quals() & TypeQual_Const) { - if (!type->isArray() && !type->isStruct()) { - cc_.reportError(param->pos(), Message_InvalidConstOnFormal); - return false; - } - } - - if (type->isVoid()) { - cc_.reportError(param->type()->pos(), Message_CannotDeclareVoid); - return false; - } - - params->set(zone_, i, type); - param->sym()->setType(type); - } - - fun->setParameterTypes(params); - - return true; - } - - Type *bindType(Expression *expr, TypeQualifiers quals) { - Local type(zone_); - - if (!expr) { - // If no type was declared, we return int32. - type = zone_->types()->getPrimitive(PrimitiveType_Int32); - } else { - expr->accept(this); - - NameProxy *proxy = expr->asNameProxy(); - assert(proxy); - - Symbol *sym = proxy->sym(); - if (!sym) - return NULL; - - if (!sym->isType()) { - cc_.reportError(expr->pos(), Message_IdentifierIsNotAType, sym->name()->chars()); - return NULL; - } - - type = sym->toType()->effective(); - } - - return zone_->types()->qualify(type, quals); - } - - protected: - Zone *zone_; - PoolAllocator &pool_; - CompileContext &cc_; - TranslationUnit *unit_; -}; - -class GlobalBuilder : public NameBinderBase -{ - typedef ScopedRoot ScopedDescriptor; - struct PrimitiveFill { - ScopedDescriptor *desc; - BoxedPrimitive prim; - - PrimitiveFill(ScopedDescriptor *desc, BoxedPrimitive prim) - : desc(desc), - prim(prim) - { - } - }; - - public: - GlobalBuilder(Zone *zone, CompileContext &cc, TranslationUnit *unit) - : NameBinderBase(zone, cc, unit), - globalScope_(unit->globalScope()) - { - } - - Struct *build() { - for (size_t i = 0; i < unit_->tree()->imports()->length(); i++) - unit_->tree()->imports()->at(i)->accept(this); - - // Next, enter the global scope and walk all statements. - for (size_t i = 0; i < unit_->tree()->statements()->length(); i++) { - Statement *stmt = unit_->tree()->statements()->at(i); - stmt->accept(this); - } - - Local globals(zone_, createGlobals()); - if (!globals) - return NULL; - - populate(globals); - - return globals; - } - - private: - Struct *createGlobals() { - Local descs(zone_, FixedArray::New(zone_, descriptors_.length(), Heap::Tenure_Old)); - if (!descs) - return NULL; - - for (unsigned i = 0; i < descriptors_.length(); i++) - descs->set(zone_, i, *descriptors_[i]); - - Local name(zone_, unit_->module()->name()); - Local type(zone_, StructType::New(zone_, name, descs)); - if (!type) - return NULL; - - Local map(zone_, type->newMap()); - return Struct::New(zone_, map, Heap::Tenure_Old); - } - - void populate(Handle globals) { - // This is a huge hack, but it's probably better than a full extra - // pass. The problem is that until we have all the descriptors, we - // can't start setting constexprs in the struct, and we need to set - // them now so they're available during full semantic analysis. To - // work around this, we build up the list of values we need to set - // as we build up descriptors, and then populate the global struct - // with this set. - // - // Note, this code is also kind of dead until we support linking. We - // handle constexprs explicitly in importFrom(), by sniffing out the - // symbol type. That code will go away later. - for (size_t i = 0; i < primitiveFills_.length(); i++) { - const PrimitiveFill &fill = primitiveFills_[i]; - assert(fill.prim.type() == PrimitiveType_Int32); - globals->set(*fill.desc, fill.prim.toInt()); - } - } - - public: - void visitEnumStatement(EnumStatement *stmt) { - if (stmt->visibility() != TOK_PUBLIC || !stmt->name()) - return; - - Local type(zone_, zone_->types()->getType()); - Descriptor *desc = Descriptor::New(zone_, stmt->name(), type, Descriptor::Public); - if (!desc) - return; - if (!descriptors_.append(new (pool_) ScopedRoot(desc))) - return; - - for (size_t i = 0; i < stmt->entries()->length(); i++) { - EnumStatement::Entry &e = stmt->entries()->at(i); - ScopedDescriptor *desc = new (pool_) ScopedDescriptor(Descriptor::New(zone_, e.name, e.sym->type(), Descriptor::Public)); - if (!desc || !descriptors_.append(desc)) - return; - - // Save the value so we can export it immediately. - if (!primitiveFills_.append(PrimitiveFill(desc, e.sym->value()))) - return; - } - } - - void visitNameProxy(NameProxy *proxy) { - Scope *scope = unit_->globalScope(); - Symbol *sym = scope->lookup(proxy->name()); - if (!sym) - cc_.reportError(proxy->pos(), Message_IdentifierNotFound, proxy->name()->chars()); - proxy->bind(sym); - } - - void visitFunctionStatement(FunctionStatement *node) { - Local type(zone_, node->sym()->type()); - if (!fillFunctionType(type, node->signature())) - return; - - unsigned offset; - if (!builder_.add(type, &offset)) - return; - - Descriptor::Visibility visibility = (node->token() == TOK_NATIVE || node->token() == TOK_FORWARD) - ? Descriptor::Public - : Descriptor::Private; - - Descriptor *desc = Descriptor::New(zone_, node->name(), type, visibility); - if (!desc) - return; - if (!descriptors_.append(new (pool_) ScopedRoot(desc))) - return; - } - - void visitVariableDeclaration(VariableDeclaration *node) { - Local type(zone_, buildVariableType(node)); - if (!type) - return; - - // If we're in global scope, and we received a |const| qualifier, then - // we extend this to array references as well. This is to sort of mimic - // SP1 functionality, but also means that you cannot have changeable - // const pointers in global scope. - if ((node->quals() & TypeQual_Const) && type->isArray()) { - if ((type = zone_->types()->qualify(type, TypeQual_Const)) == NULL) - return; - } - - node->sym()->setType(type); - - unsigned offset; - if (!builder_.add(type, &offset)) - return; - - Descriptor *desc = Descriptor::New(zone_, node->name(), type, Descriptor::Private); - if (!desc) - return; - if (!descriptors_.append(new (pool_) ScopedRoot(desc))) - return; - } - - void visitImportStatement(ImportStatement *node) { - if (!node->names()) { - PathComponent *path = node->path(); - while (path->next) - path = path->next; - - // Imports automatically go into the importScope. For now, we - // error if there are any duplicate symbols entered into the - // importScope. - if (unit_->importScope()->localLookup(path->name)) { - cc_.reportError(node->pos(), Message_DuplicateImport, path->name->chars()); - return; - } - - Local type(zone_, zone_->types()->getImportable()); - ImportSymbol *sym = new (pool_) ImportSymbol(unit_->importScope(), path->name, node->pos(), type, node->importIndex()); - if (!unit_->importScope()->addSymbol(sym)) - return; - } else if (node->importAllNames()) { - Local importable(zone_, unit_->module()->getImportAt(node->importIndex())); - if (importable->isPackage()) { - assert(false); - } else { - Local module(zone_, Module::cast(importable)); - if (!module->globals()) - importAllFromTranslationUnit(node, module->translationUnit()); - else - assert(false); - } - } else { - Local importable(zone_, unit_->module()->getImportAt(node->importIndex())); - if (importable->isPackage()) { - assert(false); - } else { - Local module(zone_, Module::cast(importable)); - - // Always grab from the translation unit if we can, for now. - if (module->translationUnit()) - importFromTranslationUnit(node, module->translationUnit()); - else - assert(false); - } - } - } - - private: - bool importFrom(ImportStatement *node, Symbol *import) { - assert(import->type()); - - // Check for duplicate import names. - if (unit_->importScope()->localLookup(import->name())) { - cc_.reportError(node->pos(), Message_DuplicateImport, import->name()->chars()); - return false; - } - - Symbol *sym; - if (import->isType()) { - // Fuck it, make a TypeSymbol instead of an ImplicitSymbol. This - // makes bindType() much simpler. We may run into problems - // implementing linking, but it seems unlikely, as long as we are - // exporting Types in module structs. - Local type(zone_, zone_->types()->getType()); - sym = new (pool_) TypeSymbol(unit_->importScope(), - import->name(), - type, - import->toType()->effective()); - } else if (import->isConstant()) { - // Same deal. No reason to lookup in the module globals if we - // already have a symbol. - sym = new (pool_) ConstantSymbol(unit_->importScope(), - import->name(), - node->pos(), - import->type(), - import->toConstant()->value()); - } else { - sym = new (pool_) ImplicitSymbol(unit_->importScope(), - import->name(), - node->pos(), - import->type(), - node->importIndex()); - } - - if (!unit_->importScope()->addSymbol(sym)) - return false; - - return true; - } - - void importFromTranslationUnit(ImportStatement *node, TranslationUnit *other) { - assert(!other->failed()); - - for (size_t i = 0; i < node->names()->length(); i++) { - Handle name = *node->names()->at(i); - - // See if the name exists in the other translation unit. - Symbol *import = other->globalScope()->findExported(name); - if (!import) { - FixedArray *path = unit_->module()->importPathAt(node->importIndex()); - cc_.reportError(node->pos(), Message_NotFoundInImportable, - name->chars(), - String::cast(path->at(path->length() - 1))->chars()); - return; - } - - if (!importFrom(node, import)) - return; - } - } - - void importAllFromTranslationUnit(ImportStatement *node, TranslationUnit *other) { - assert(!other->failed()); - - for (size_t i = 0; i < other->globalScope()->exported()->length(); i++) { - Symbol *import = other->globalScope()->exported()->at(i); - if (!importFrom(node, import)) - return; - } - } - - private: - StructureBufferBuilder builder_; - Vector *> descriptors_; - GlobalScope *globalScope_; - Vector primitiveFills_; -}; - -class NameBinder : public NameBinderBase -{ - public: - class AutoLinkScope - { - public: - AutoLinkScope(AutoLinkScope **prevp, Scope *scope) - : prevp_(prevp), - prev_(*prevp), - scope_(scope) - { - if (scope_) { - *prevp_ = this; - if (prev_) - scope_->setParent(prev_->scope()); - } - } - ~AutoLinkScope() { - if (scope_) { - assert(*prevp_ == this); - *prevp_ = prev_; - } - } - Scope *scope() const { - return scope_; - } - - private: - AutoLinkScope **prevp_; - AutoLinkScope *prev_; - Scope *scope_; - }; - - public: - NameBinder(Zone *zone, CompileContext &cc, TranslationUnit *unit) - : NameBinderBase(zone, cc, unit), - link_(NULL) - { - } - - void bind() { - // Start with the import scope, and collect all imported names. - AutoLinkScope importScope(&link_, unit_->importScope()); - - for (size_t i = 0; i < unit_->tree()->imports()->length(); i++) - unit_->tree()->imports()->at(i)->accept(this); - - // Next, enter the global scope and walk all statements. - AutoLinkScope globalScope(&link_, unit_->globalScope()); - for (size_t i = 0; i < unit_->tree()->statements()->length(); i++) { - Statement *stmt = unit_->tree()->statements()->at(i); - stmt->accept(this); - } - } - - void visitVariableDeclaration(VariableDeclaration *node) { - // Bind the initializer before registering the declaration, so that we - // can error on bogus initializers (new x = x). - if (node->initialization()) - node->initialization()->accept(this); - - // If the node already has a symbol (meaning it was a global), then - // we don't have to do anything more. - if (node->sym()) - return; - - Local type(zone_, buildVariableType(node)); - if (!type) - return; - - VariableSymbol *sym = new (pool_) VariableSymbol(link_->scope(), node->name(), node->pos(), type); - node->setSymbol(sym); - - if (Symbol *other = link_->scope()->localLookup(sym->name())) { - // Report, but allow errors to continue. - cc_.reportError(sym->pos(), Message_RedeclaredName, - sym->name()->chars(), - other->pos().line, - other->pos().col); - return; - } - - link_->scope()->addSymbol(sym); - } - void visitEnumStatement(EnumStatement *node) { - } - void visitFunctionStatement(FunctionStatement *node) { - AutoLinkScope argScope(&link_, node->argScope()); - AutoLinkScope varScope(&link_, node->varScope()); - - // If the function is in the global scope, we've already computed its - // type. - if (node->sym()->scope()->kind() != Scope::GLOBAL) { - Local type(zone_, node->sym()->type()); - if (!fillFunctionType(type, node->signature())) - return; - } - - if (node->body()) - node->body()->accept(this); - } - void visitStructureStatement(StructureStatement *node) { - assert(false); - } - void visitFunctionTypeStatement(FunctionTypeStatement *node) { - assert(false); - } - - void visitNameProxy(NameProxy *proxy) { - // We could have already bound globals. - if (proxy->sym()) - return; - - Scope *scope = link_->scope(); - Symbol *sym = scope->lookup(proxy->name()); - if (!sym) - cc_.reportError(proxy->pos(), Message_IdentifierNotFound, proxy->name()->chars()); - proxy->bind(sym); - } - void visitAssignment(Assignment *node) { - node->lvalue()->accept(this); - node->expression()->accept(this); - } - void visitBinaryExpression(BinaryExpression *node) { - node->left()->accept(this); - node->right()->accept(this); - } - void visitReturnStatement(ReturnStatement *node) { - node->expression()->accept(this); - } - void visitForStatement(ForStatement *node) { - AutoLinkScope scope(&link_, node->scope()); - - if (node->initialization()) - node->initialization()->accept(this); - if (node->condition()) - node->condition()->accept(this); - if (node->update()) - node->update()->accept(this); - node->body()->accept(this); - } - void visitBlockStatement(BlockStatement *node) { - AutoLinkScope scope(&link_, node->scope()); - for (size_t i = 0; i < node->statements()->length(); i++) - node->statements()->at(i)->accept(this); - } - void visitIntegerLiteral(IntegerLiteral *node) { - } - void visitExpressionStatement(ExpressionStatement *node) { - node->expression()->accept(this); - } - void visitCallExpression(CallExpression *node) { - node->callee()->accept(this); - for (size_t i = 0; i < node->arguments()->length(); i++) - node->arguments()->at(i)->accept(this); - } - void visitIfStatement(IfStatement *node) { - node->condition()->accept(this); - node->ifTrue()->accept(this); - if (node->ifFalse()) - node->ifFalse()->accept(this); - } - void visitIndexExpression(IndexExpression *node) { - node->left()->accept(this); - node->right()->accept(this); - } - void visitFloatLiteral(FloatLiteral *node) { - } - void visitWhileStatement(WhileStatement *node) { - node->condition()->accept(this); - node->body()->accept(this); - } - void visitBreakStatement(BreakStatement *node) { - } - void visitContinueStatement(ContinueStatement *node) { - } - void visitStringLiteral(StringLiteral *node) { - } - void visitIncDecExpression(IncDecExpression *node) { - node->expression()->accept(this); - } - void visitUnaryExpression(UnaryExpression *node) { - if (node->tag()) - node->tag()->accept(this); - node->expression()->accept(this); - } - void visitTernaryExpression(TernaryExpression *node) { - node->condition()->accept(this); - node->left()->accept(this); - node->right()->accept(this); - } - void visitBooleanLiteral(BooleanLiteral *node) { - } - void visitFieldExpression(FieldExpression *node) { - node->left()->accept(this); - } - void visitSwitchStatement(SwitchStatement *node) { - node->expression()->accept(this); - node->defaultCase()->accept(this); - for (size_t i = 0; i < node->cases()->length(); i++) { - // We don't test case expressions because they are literals. - Case *c = node->cases()->at(i); - c->statement()->accept(this); - } - } - void visitArrayLiteral(ArrayLiteral *node) { - for (size_t i = 0; i < node->expressions()->length(); i++) - node->expressions()->at(i)->accept(this); - } - void visitImportStatement(ImportStatement *node) { - } - - private: - AutoLinkScope *link_; -}; - -void -ke::BindGlobals(Zone *zone, CompileContext &cc, TranslationUnit *unit) -{ - AutoEnterCompileContext enter(cc, unit); - - GlobalBuilder builder(zone, cc, unit); - Local globals(zone, builder.build()); - if (!globals || unit->failed()) - return; - - unit->module()->setGlobals(globals); -} - -void -ke::BindNamesAndTypes(Zone *zone, CompileContext &cc, TranslationUnit *unit) -{ - AutoEnterCompileContext enter(cc, unit); - NameBinder binder(zone, cc, unit); - binder.bind(); -} +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#include "../Strings.h" +#include "../Zone.h" +#include "../TypeManager.h" +#include "NameBinding.h" +#include "Scopes.h" +#include "ConstantEvaluator.h" +#include "../Spaces-inl.h" +#include "../Heap-inl.h" + +using namespace ke; + +// Create symbol tables and symbols for every scope and declaration. For type +// declarations and methods, we also pre-allocate a Type object, so that +// circular dependencies between types will work. +class NamePopulator : public AstVisitor +{ + private: + // This class is analagous to a Scope, however, it allows us to create + // scopes lazily. If a scope is requested, and the current environment + // has no scope, then a scope is created. If no scope is ever requested, + // a scope does not need to be created. + class SymbolEnv + { + public: + SymbolEnv(SymbolEnv **prevp, Scope *scope = NULL) + : envp_(prevp), + prev_(*prevp), + scope_(scope) + { + *envp_ = this; + } + ~SymbolEnv() { + *envp_ = prev_; + } + + Scope *scope() { + return scope_; + } + void setScope(Scope *scope) { + assert(!scope_); + scope_ = scope; + } + SymbolEnv *prev() const { + return prev_; + } + + private: + SymbolEnv **envp_; + SymbolEnv *prev_; + Scope *scope_; + }; + + enum ReturnStatus { + NoReturn, + VoidReturn, + ValueReturn + }; + + class FunctionScope + { + public: + FunctionScope(FunctionScope **prevp) + : prevp_(prevp), + prev_(*prevp), + status_(NoReturn) + { + *prevp_ = this; + } + ~FunctionScope() { + *prevp_ = prev_; + } + ReturnStatus returns() const { + return status_; + } + void returns(ReturnStatus status) { + if (status > status_) + status_ = status; + } + private: + FunctionScope **prevp_; + FunctionScope *prev_; + ReturnStatus status_; + }; + + public: + NamePopulator(Zone *zone, CompileContext &cc, TranslationUnit *unit) + : zone_(zone), + pool_(zone->pool()), + cc_(cc), + unit_(unit), + env_(NULL), + fun_(NULL) + { + } + + void analyze() { + ImportScope *importScope = ImportScope::New(pool_); + if (!importScope) + return; + + // At the import level, we declare system types. + if (!declareSystemTypes(importScope)) + return; + + GlobalScope *globalScope = GlobalScope::New(pool_, importScope); + if (!globalScope) + return; + + unit_->setImportScope(importScope); + unit_->setGlobalScope(globalScope); + + SymbolEnv env(&env_, globalScope); + + for (size_t i = 0; i < unit_->tree()->statements()->length(); i++) { + Statement *stmt = unit_->tree()->statements()->at(i); + stmt->accept(this); + } + + return; + } + + bool declareSystemType(ImportScope *scope, const char *name, Handle effective) { + Local tag(zone_, zone_->makeSymbol(name)); + if (!tag) + return false; + + Local type(zone_, zone_->types()->getType()); + + TypeSymbol *sym = new (pool_) TypeSymbol(scope, tag, type, effective); + return scope->addSymbol(sym); + } + + bool declareSystemType(ImportScope *scope, const char *name, PrimitiveType prim) { + Local primitive(zone_, zone_->types()->getPrimitive(prim)); + assert(primitive); + return declareSystemType(scope, name, primitive); + } + + bool declareSystemTypes(ImportScope *scope) { + if (!declareSystemType(scope, "float", PrimitiveType_Float)) + return false; + if (!declareSystemType(scope, "_", PrimitiveType_Int32)) + return false; + if (!declareSystemType(scope, "int", PrimitiveType_Int32)) + return false; + if (!declareSystemType(scope, "bool", PrimitiveType_Bool)) + return false; + if (!declareSystemType(scope, "char", PrimitiveType_Char)) + return false; + + Local voidType(zone_, zone_->types()->getVoid()); + if (!declareSystemType(scope, "void", voidType)) + return false; + + return true; + } + + void visitVariableDeclaration(VariableDeclaration *node) { + Scope *scope = env_->scope(); + if (!scope || scope->kind() != Scope::GLOBAL) { + if ((scope = getOrCreateBlockScope()) == NULL) + return; + } + + if (scope->kind() == Scope::GLOBAL) { + // The symbol must be added now, since it could be an export. + VariableSymbol *sym = new (pool_) VariableSymbol(scope, node->name(), node->pos()); + node->setSymbol(sym); + + if (!registerName(scope, sym)) + return; + } else { + // We ensure the scope will exist at the name binding phase, but + // we don't actually register the variable symbol yet. Otherwise, + // when binding the initializer, we could encounter: + // new x = x; + // + // And we don't necessarily want this to bind. + } + + if (node->initialization()) + node->initialization()->accept(this); + } + + void visitEnumStatement(EnumStatement *node) { + Local name(zone_, node->name()); + Local type(zone_); + if (name) + type = zone_->types()->newEnum(name); + else + type = zone_->types()->getPrimitive(PrimitiveType_Int32); + + if (!type) + return; + + Scope *scope = getGlobalOrCreateBlockScope(); + if (!scope) + return; + + if (name) { + Local symType(zone_, zone_->types()->getType()); + TypeSymbol *sym = new (pool_) TypeSymbol(scope, name, symType, type); + if (!registerName(scope, sym)) + return; + + if (scope->kind() == Scope::GLOBAL) { + if (!unit_->globalScope()->exported()->append(sym)) + return; + } + } + + // We want each field of the expression to qualify as a constant expression. + if ((type = zone_->types()->qualify(type, TypeQual_ConstExpr)) == NULL) + return; + + int value = 0; + for (size_t i = 0; i < node->entries()->length(); i++) { + EnumStatement::Entry &entry = node->entries()->at(i); + BoxedPrimitive box = BoxedPrimitive::Int(value); + if (entry.expr) { + if (!EvaluateForConstant(entry.expr, &box) || box.type() != PrimitiveType_Int32) { + cc_.reportError(entry.expr->pos(), Message_EnumFieldNotConstant); + return; + } + + assert(box.type() == PrimitiveType_Int32); + value = box.toInt(); + } + + ConstantSymbol *sym = new (pool_) ConstantSymbol(scope, entry.name, entry.pos, type, box); + if (!registerName(scope, sym)) + return; + if (scope->kind() == Scope::GLOBAL) { + if (!unit_->globalScope()->exported()->append(sym)) + return; + } + + entry.sym = sym; + value++; + } + } + + void visitFunctionStatement(FunctionStatement *node) { + // We create function types ahead of time, not for any particular + // reason. It makes things consistent with FunctionTypeStatement, + // which actually does produce a type. + Local type(zone_, FunctionType::New(zone_, node->name(), node->token())); + if (!type) + return; + + assert(env_->scope()); + + // Function statements are always named, so add the name to the outer scope. + FunctionSymbol *sym = new (pool_) FunctionSymbol(env_->scope(), node->name(), node->pos(), type); + if (!registerName(env_->scope(), sym)) + return; + + if (node->token() == TOK_PUBLIC || + node->token() == TOK_FORWARD || + node->token() == TOK_NATIVE) + { + // We're in the global scope, for sure. + assert(env_->scope()->kind() == Scope::GLOBAL); + + if (!unit_->globalScope()->exported()->append(sym)) + return; + } + + node->setSymbol(sym); + + // Scope for the function's arguments. + SymbolEnv argEnv(&env_); + if (!registerArguments(node->signature())) + return; + + // Scope for function body. + SymbolEnv localEnv(&env_); + FunctionScope fun(&fun_); + if (node->body()) + node->body()->accept(this); + + // Function statements have quirky semantics around their return + // type. For reasonable compatibility with SP1, we use the following + // heuristics for when no explicit type is declared. + if (!node->signature().returnType()) { + Local type(zone_); + if (node->token() == TOK_FORWARD || node->token() == TOK_NATIVE) { + // If the function type is a native or forward, we always + // assume it returns an int32. + type = zone_->types()->getPrimitive(PrimitiveType_Int32); + } else if (fun.returns() != ValueReturn) { + // This function never returns a value, so mark it as + // returning void. + type = zone_->types()->getVoid(); + } + // No need to NULL check here, we'll catch OOM later. + node->sym()->type()->setReturnType(type); + } + + node->setScopes(argEnv.scope(), localEnv.scope()); + } + + void visitStructureStatement(StructureStatement *node) { + assert(false); + } + + void visiFunctionTypeStatementt(FunctionTypeStatement *node) { + assert(false); + } + + void visitNameProxy(NameProxy *name) { + } + void visitAssignment(Assignment *node) { + node->lvalue()->accept(this); + node->expression()->accept(this); + } + void visitBinaryExpression(BinaryExpression *node) { + node->left()->accept(this); + node->right()->accept(this); + } + void visitReturnStatement(ReturnStatement *node) { + if (node->expression()) + fun_->returns(ValueReturn); + else + fun_->returns(VoidReturn); + } + void visitForStatement(ForStatement *node) { + SymbolEnv env(&env_); + + if (node->initialization()) + node->initialization()->accept(this); + if (node->condition()) + node->condition()->accept(this); + if (node->update()) + node->update()->accept(this); + node->body()->accept(this); + + node->setScope(env.scope()); + } + void visitBlockStatement(BlockStatement *node) { + if (node->type() == TOK_NEW) { + // Don't create a symbol environment for this block, since it + // was created solely to house multiple variable decls. + for (size_t i = 0; i < node->statements()->length(); i++) + node->statements()->at(i)->accept(this); + } else { + assert(node->type() == TOK_LBRACE); + + SymbolEnv env(&env_); + + for (size_t i = 0; i < node->statements()->length(); i++) + node->statements()->at(i)->accept(this); + + if (node->type() == TOK_NEW) { + assert(!env.scope()); + return; + } + + node->setScope(env.scope()); + } + } + void visitIntegerLiteral(IntegerLiteral *node) { + } + void visitExpressionStatement(ExpressionStatement *node) { + node->expression()->accept(this); + } + void visitCallExpression(CallExpression *node) { + node->callee()->accept(this); + for (size_t i = 0; i < node->arguments()->length(); i++) + node->arguments()->at(i)->accept(this); + } + void visitIfStatement(IfStatement *node) { + node->ifTrue()->accept(this); + if (node->ifFalse()) + node->ifFalse()->accept(this); + } + void visitIndexExpression(IndexExpression *node) { + node->left()->accept(this); + node->right()->accept(this); + } + void visitFloatLiteral(FloatLiteral *node) { + } + void visitWhileStatement(WhileStatement *node) { + node->condition()->accept(this); + node->body()->accept(this); + } + void visitBreakStatement(BreakStatement *node) { + } + void visitContinueStatement(ContinueStatement *node) { + } + void visitStringLiteral(StringLiteral *node) { + } + void visitIncDecExpression(IncDecExpression *node) { + node->expression()->accept(this); + } + void visitUnaryExpression(UnaryExpression *node) { + node->expression()->accept(this); + } + void visitTernaryExpression(TernaryExpression *node) { + assert(false); + } + void visitBooleanLiteral(BooleanLiteral *node) { + } + void visitFieldExpression(FieldExpression *node) { + } + void visitSwitchStatement(SwitchStatement *node) { + if (node->defaultCase()) + node->defaultCase()->accept(this); + + for (size_t i = 0; i < node->cases()->length(); i++) + node->cases()->at(i)->statement()->accept(this); + } + void visitArrayLiteral(ArrayLiteral *node) { + } + void visitImportStatement(ImportStatement *node) { + } + + private: + Scope *getGlobalOrCreateBlockScope() { + if (env_->scope()) { + assert(env_->scope()->kind() == Scope::BLOCK || + env_->scope()->kind() == Scope::GLOBAL); + return env_->scope(); + } + return getOrCreateBlockScope(); + } + Scope *getOrCreateBlockScope() { + if (env_->scope()) { + assert(env_->scope()->kind() == Scope::BLOCK); + return env_->scope(); + } + + LocalScope *scope = LocalScope::New(pool_); + if (!scope) + return NULL; + + env_->setScope(scope); + return scope; + } + + bool registerName(Scope *scope, Symbol *sym) { + if (!scope) + return false; + + if (Symbol *other = scope->localLookup(sym->name())) { + // Report, but allow errors to continue. + cc_.reportError(sym->pos(), Message_RedeclaredName, + sym->name()->chars(), + other->pos().line, + other->pos().col); + return true; + } + + return scope->addSymbol(sym); + } + + bool registerArguments(const FunctionSignature &sig) { + if (!sig.parameters()->length()) + return true; + + ArgumentScope *argScope = ArgumentScope::New(pool_); + if (!argScope) + return false; + env_->setScope(argScope); + + for (size_t i = 0; i < sig.parameters()->length(); i++) { + Parameter *param = sig.parameters()->at(i); + VariableSymbol *sym = new (pool_) VariableSymbol(argScope, param->name(), param->pos()); + if (!registerName(env_->scope(), sym)) + return false; + param->setSymbol(sym); + } + + return true; + } + + private: + Zone *zone_; + PoolAllocator &pool_; + CompileContext &cc_; + TranslationUnit *unit_; + SymbolEnv *env_; + FunctionScope *fun_; +}; + +void +ke::PopulateNamesAndTypes(Zone *zone, CompileContext &cc, TranslationUnit *unit) +{ + AutoEnterCompileContext enter(cc, unit); + NamePopulator populator(zone, cc, unit); + populator.analyze(); +} + +static const int INFER_ARRAY_SIZE = 0; +static const int EVAL_ARRAY_SIZE = -1; +static const int DYNAMIC_ARRAY_SIZE = -2; + +class NameBinderBase : public AstVisitor +{ + public: + NameBinderBase(Zone *zone, CompileContext &cc, TranslationUnit *unit) + : zone_(zone), + pool_(zone->pool()), + cc_(cc), + unit_(unit) + { + } + + protected: + Type *buildVariableType(VariableDeclaration *node) { + Local type(zone_, bindType(node->type(), node->quals())); + if (!type) + return NULL; + + // We don't allow array typedefs yet. + assert(!type->isArray()); + + if (node->dims()) { + int dims[MAX_ARRAY_DEPTH]; + int levels = evaluateDimensions(node->pos(), type, node->dims(), dims); + if (levels < 0) + return NULL; + + // Do some extra inference based on the initializer, if present. + Expression *init = node->initialization(); + if (init) { + if (init->isArrayLiteral() && init->toArrayLiteral()->isFixed()) { + Local contained(zone_, type->isArray() + ? ArrayType::cast(type)->contained() + : type); + if (!inferFixedArrayDimensions(init->toArrayLiteral(), contained, dims, levels)) + return NULL; + } else if (init->isStringLiteral()) { + dims[levels - 1] = init->toStringLiteral()->string()->length() + 1; + } + } + + // If we got extra dimensions, we need to build a new type. + if (node->dims()) { + if ((type = buildArrayType(type, node->quals(), dims, node->dims()->length())) == NULL) + return NULL; + } + } + + return type; + } + + int evaluateDimensions(const SourcePosition &pos, Handle typeArg, ExpressionList *exprs, int dims[MAX_ARRAY_DEPTH]) { + unsigned level = 0; + Local type(zone_, typeArg); + while (type->isArray()) { + ArrayType *atype = ArrayType::cast(type); + dims[level] = atype->isFixedLength() + ? atype->fixedLength() + : DYNAMIC_ARRAY_SIZE; + type = atype->contained(); + level++; + } + + if (!exprs) + return level; + + if (level + exprs->length() > MAX_ARRAY_DEPTH) { + cc_.reportError(pos, Message_MaximumArrayDepth); + return -1; + } + + bool hadEmptySize = false; + for (size_t i = 0; i < exprs->length(); i++) { + Expression *expr = exprs->at(i); + if (!expr) { + dims[i] = INFER_ARRAY_SIZE; + hadEmptySize = true; + continue; + } + + BoxedPrimitive box; + if (EvaluateForConstant(expr, &box) && box.type() == PrimitiveType_Int32) { + int size = box.toInt(); + if (size <= 0) { + cc_.reportError(expr->pos(), Message_BadArraySize); + return false; + } + + dims[i] = size; + continue; + } + + if (hadEmptySize) { + cc_.reportError(expr->pos(), Message_BadDynamicInitializer); + return false; + } + + dims[i] = EVAL_ARRAY_SIZE; + } + + return level + exprs->length(); + } + + // This function computes missing dimension sizes based on a literal + // initializer. We don't really handle any size rules here; we just + // do a really dumb inference. + bool inferFixedArrayDimensions(ArrayLiteral *lit, Handle contained, int dims[MAX_ARRAY_DEPTH], int levels) { + for (int i = 0; i < levels; i++) { + if (dims[i] == EVAL_ARRAY_SIZE || dims[i] == DYNAMIC_ARRAY_SIZE) { + // The following are invalid: + // new x[] = {...} + // new Type:x = {...} // where Type is a non-fixed array type + cc_.reportError(lit->pos(), Message_CannotInitializeEvalArray); + return false; + } + + if (dims[i] == INFER_ARRAY_SIZE) { + // Character arrays are always dynamic when inferred from string literals. + if (i == levels - 1 && contained->isChar()) + dims[i] = DYNAMIC_ARRAY_SIZE; + else + dims[i] = lit->expressions()->length(); + } + + if (i != levels - 1) { + // Fixed array literals are guaranteed to have at least one expression. + Expression *next = lit->expressions()->at(0); + if (!next->isArrayLiteral()) { + // Whelp... this array is just malformed. Not the best error message + // but whatever. + cc_.reportError(next->pos(), Message_ArraySizeCannotBeDetermined); + return false; + } + lit = next->toArrayLiteral(); + } + } + + return true; + } + + ArrayType *buildArrayType(Handle contained, TypeQualifiers quals, int dims[MAX_ARRAY_DEPTH], size_t postlevels) { + Local type(zone_, contained); + + for (size_t i = postlevels - 1; i < postlevels; i--) { + int size = dims[i] > 0 + ? dims[i] + : ArrayType::DYNAMIC_ARRAY_SIZE; + + if ((type = zone_->types()->newArray(contained, size)) == NULL) + return NULL; + if ((type = zone_->types()->qualify(type, quals)) == NULL) + return NULL; + } + return ArrayType::cast(type); + } + + bool fillFunctionType(Handle fun, const FunctionSignature &sig) { + Local type(zone_); + if (!fun->returnType()) { + if ((type = bindType(sig.returnType(), TypeQual_None)) == NULL) + return false; + fun->setReturnType(type); + } + + if (sig.variadic()) + fun->setVariadic(); + + Local params(zone_, FixedArray::New(zone_, sig.parameters()->length(), Heap::Tenure_Old)); + if (!params) + return false; + + for (size_t i = 0; i < sig.parameters()->length(); i++) { + Parameter *param = sig.parameters()->at(i); + if ((type = bindType(param->type(), param->quals())) == NULL) + return false; + + assert(!type->isArray()); + + if (param->dims()) { + int dims[MAX_ARRAY_DEPTH]; + int levels = evaluateDimensions(param->pos(), type, param->dims(), dims); + if (levels < 0) + return false; + + // If we got extra dimensions, we need to build a new type. + // Unlike variables, we do not infer anything about array + // sizes from its default initializer. + if (param->dims()) { + if ((type = buildArrayType(type, param->quals(), dims, param->dims()->length())) == NULL) + return false; + } + + if (ArrayType::cast(type)->innermost()->isVoid()) { + cc_.reportError(param->type()->pos(), Message_CannotDeclareVoid); + return false; + } + } + + if (param->reference()) { + if (!type->isPod()) { + cc_.reportError(param->pos(), Message_ArrayCantBeReference); + return false; + } + if (param->quals() & TypeQual_Const) { + cc_.reportError(param->pos(), Message_CannotConstReference); + return false; + } + if ((type = zone_->types()->getReference(type, false)) == NULL) + return false; + } + if (param->quals() & TypeQual_Const) { + if (!type->isArray() && !type->isStruct()) { + cc_.reportError(param->pos(), Message_InvalidConstOnFormal); + return false; + } + } + + if (type->isVoid()) { + cc_.reportError(param->type()->pos(), Message_CannotDeclareVoid); + return false; + } + + params->set(zone_, i, type); + param->sym()->setType(type); + } + + fun->setParameterTypes(params); + + return true; + } + + Type *bindType(Expression *expr, TypeQualifiers quals) { + Local type(zone_); + + if (!expr) { + // If no type was declared, we return int32. + type = zone_->types()->getPrimitive(PrimitiveType_Int32); + } else { + expr->accept(this); + + NameProxy *proxy = expr->asNameProxy(); + assert(proxy); + + Symbol *sym = proxy->sym(); + if (!sym) + return NULL; + + if (!sym->isType()) { + cc_.reportError(expr->pos(), Message_IdentifierIsNotAType, sym->name()->chars()); + return NULL; + } + + type = sym->toType()->effective(); + } + + return zone_->types()->qualify(type, quals); + } + + protected: + Zone *zone_; + PoolAllocator &pool_; + CompileContext &cc_; + TranslationUnit *unit_; +}; + +class GlobalBuilder : public NameBinderBase +{ + typedef ScopedRoot ScopedDescriptor; + struct PrimitiveFill { + ScopedDescriptor *desc; + BoxedPrimitive prim; + + PrimitiveFill(ScopedDescriptor *desc, BoxedPrimitive prim) + : desc(desc), + prim(prim) + { + } + }; + + public: + GlobalBuilder(Zone *zone, CompileContext &cc, TranslationUnit *unit) + : NameBinderBase(zone, cc, unit), + globalScope_(unit->globalScope()) + { + } + + Struct *build() { + for (size_t i = 0; i < unit_->tree()->imports()->length(); i++) + unit_->tree()->imports()->at(i)->accept(this); + + // Next, enter the global scope and walk all statements. + for (size_t i = 0; i < unit_->tree()->statements()->length(); i++) { + Statement *stmt = unit_->tree()->statements()->at(i); + stmt->accept(this); + } + + Local globals(zone_, createGlobals()); + if (!globals) + return NULL; + + populate(globals); + + return globals; + } + + private: + Struct *createGlobals() { + Local descs(zone_, FixedArray::New(zone_, descriptors_.length(), Heap::Tenure_Old)); + if (!descs) + return NULL; + + for (unsigned i = 0; i < descriptors_.length(); i++) + descs->set(zone_, i, *descriptors_[i]); + + Local name(zone_, unit_->module()->name()); + Local type(zone_, StructType::New(zone_, name, descs)); + if (!type) + return NULL; + + Local map(zone_, type->newMap()); + return Struct::New(zone_, map, Heap::Tenure_Old); + } + + void populate(Handle globals) { + // This is a huge hack, but it's probably better than a full extra + // pass. The problem is that until we have all the descriptors, we + // can't start setting constexprs in the struct, and we need to set + // them now so they're available during full semantic analysis. To + // work around this, we build up the list of values we need to set + // as we build up descriptors, and then populate the global struct + // with this set. + // + // Note, this code is also kind of dead until we support linking. We + // handle constexprs explicitly in importFrom(), by sniffing out the + // symbol type. That code will go away later. + for (size_t i = 0; i < primitiveFills_.length(); i++) { + const PrimitiveFill &fill = primitiveFills_[i]; + assert(fill.prim.type() == PrimitiveType_Int32); + globals->set(*fill.desc, fill.prim.toInt()); + } + } + + public: + void visitEnumStatement(EnumStatement *stmt) { + if (stmt->visibility() != TOK_PUBLIC || !stmt->name()) + return; + + Local type(zone_, zone_->types()->getType()); + Descriptor *desc = Descriptor::New(zone_, stmt->name(), type, Descriptor::Public); + if (!desc) + return; + if (!descriptors_.append(new (pool_) ScopedRoot(desc))) + return; + + for (size_t i = 0; i < stmt->entries()->length(); i++) { + EnumStatement::Entry &e = stmt->entries()->at(i); + ScopedDescriptor *desc = new (pool_) ScopedDescriptor(Descriptor::New(zone_, e.name, e.sym->type(), Descriptor::Public)); + if (!desc || !descriptors_.append(desc)) + return; + + // Save the value so we can export it immediately. + if (!primitiveFills_.append(PrimitiveFill(desc, e.sym->value()))) + return; + } + } + + void visitNameProxy(NameProxy *proxy) { + Scope *scope = unit_->globalScope(); + Symbol *sym = scope->lookup(proxy->name()); + if (!sym) + cc_.reportError(proxy->pos(), Message_IdentifierNotFound, proxy->name()->chars()); + proxy->bind(sym); + } + + void visitFunctionStatement(FunctionStatement *node) { + Local type(zone_, node->sym()->type()); + if (!fillFunctionType(type, node->signature())) + return; + + unsigned offset; + if (!builder_.add(type, &offset)) + return; + + Descriptor::Visibility visibility = (node->token() == TOK_NATIVE || node->token() == TOK_FORWARD) + ? Descriptor::Public + : Descriptor::Private; + + Descriptor *desc = Descriptor::New(zone_, node->name(), type, visibility); + if (!desc) + return; + if (!descriptors_.append(new (pool_) ScopedRoot(desc))) + return; + } + + void visitVariableDeclaration(VariableDeclaration *node) { + Local type(zone_, buildVariableType(node)); + if (!type) + return; + + // If we're in global scope, and we received a |const| qualifier, then + // we extend this to array references as well. This is to sort of mimic + // SP1 functionality, but also means that you cannot have changeable + // const pointers in global scope. + if ((node->quals() & TypeQual_Const) && type->isArray()) { + if ((type = zone_->types()->qualify(type, TypeQual_Const)) == NULL) + return; + } + + node->sym()->setType(type); + + unsigned offset; + if (!builder_.add(type, &offset)) + return; + + Descriptor *desc = Descriptor::New(zone_, node->name(), type, Descriptor::Private); + if (!desc) + return; + if (!descriptors_.append(new (pool_) ScopedRoot(desc))) + return; + } + + void visitImportStatement(ImportStatement *node) { + if (!node->names()) { + PathComponent *path = node->path(); + while (path->next) + path = path->next; + + // Imports automatically go into the importScope. For now, we + // error if there are any duplicate symbols entered into the + // importScope. + if (unit_->importScope()->localLookup(path->name)) { + cc_.reportError(node->pos(), Message_DuplicateImport, path->name->chars()); + return; + } + + Local type(zone_, zone_->types()->getImportable()); + ImportSymbol *sym = new (pool_) ImportSymbol(unit_->importScope(), path->name, node->pos(), type, node->importIndex()); + if (!unit_->importScope()->addSymbol(sym)) + return; + } else if (node->importAllNames()) { + Local importable(zone_, unit_->module()->getImportAt(node->importIndex())); + if (importable->isPackage()) { + assert(false); + } else { + Local module(zone_, Module::cast(importable)); + if (!module->globals()) + importAllFromTranslationUnit(node, module->translationUnit()); + else + assert(false); + } + } else { + Local importable(zone_, unit_->module()->getImportAt(node->importIndex())); + if (importable->isPackage()) { + assert(false); + } else { + Local module(zone_, Module::cast(importable)); + + // Always grab from the translation unit if we can, for now. + if (module->translationUnit()) + importFromTranslationUnit(node, module->translationUnit()); + else + assert(false); + } + } + } + + private: + bool importFrom(ImportStatement *node, Symbol *import) { + assert(import->type()); + + // Check for duplicate import names. + if (unit_->importScope()->localLookup(import->name())) { + cc_.reportError(node->pos(), Message_DuplicateImport, import->name()->chars()); + return false; + } + + Symbol *sym; + if (import->isType()) { + // Fuck it, make a TypeSymbol instead of an ImplicitSymbol. This + // makes bindType() much simpler. We may run into problems + // implementing linking, but it seems unlikely, as long as we are + // exporting Types in module structs. + Local type(zone_, zone_->types()->getType()); + sym = new (pool_) TypeSymbol(unit_->importScope(), + import->name(), + type, + import->toType()->effective()); + } else if (import->isConstant()) { + // Same deal. No reason to lookup in the module globals if we + // already have a symbol. + sym = new (pool_) ConstantSymbol(unit_->importScope(), + import->name(), + node->pos(), + import->type(), + import->toConstant()->value()); + } else { + sym = new (pool_) ImplicitSymbol(unit_->importScope(), + import->name(), + node->pos(), + import->type(), + node->importIndex()); + } + + if (!unit_->importScope()->addSymbol(sym)) + return false; + + return true; + } + + void importFromTranslationUnit(ImportStatement *node, TranslationUnit *other) { + assert(!other->failed()); + + for (size_t i = 0; i < node->names()->length(); i++) { + Handle name = *node->names()->at(i); + + // See if the name exists in the other translation unit. + Symbol *import = other->globalScope()->findExported(name); + if (!import) { + FixedArray *path = unit_->module()->importPathAt(node->importIndex()); + cc_.reportError(node->pos(), Message_NotFoundInImportable, + name->chars(), + String::cast(path->at(path->length() - 1))->chars()); + return; + } + + if (!importFrom(node, import)) + return; + } + } + + void importAllFromTranslationUnit(ImportStatement *node, TranslationUnit *other) { + assert(!other->failed()); + + for (size_t i = 0; i < other->globalScope()->exported()->length(); i++) { + Symbol *import = other->globalScope()->exported()->at(i); + if (!importFrom(node, import)) + return; + } + } + + private: + StructureBufferBuilder builder_; + Vector *> descriptors_; + GlobalScope *globalScope_; + Vector primitiveFills_; +}; + +class NameBinder : public NameBinderBase +{ + public: + class AutoLinkScope + { + public: + AutoLinkScope(AutoLinkScope **prevp, Scope *scope) + : prevp_(prevp), + prev_(*prevp), + scope_(scope) + { + if (scope_) { + *prevp_ = this; + if (prev_) + scope_->setParent(prev_->scope()); + } + } + ~AutoLinkScope() { + if (scope_) { + assert(*prevp_ == this); + *prevp_ = prev_; + } + } + Scope *scope() const { + return scope_; + } + + private: + AutoLinkScope **prevp_; + AutoLinkScope *prev_; + Scope *scope_; + }; + + public: + NameBinder(Zone *zone, CompileContext &cc, TranslationUnit *unit) + : NameBinderBase(zone, cc, unit), + link_(NULL) + { + } + + void bind() { + // Start with the import scope, and collect all imported names. + AutoLinkScope importScope(&link_, unit_->importScope()); + + for (size_t i = 0; i < unit_->tree()->imports()->length(); i++) + unit_->tree()->imports()->at(i)->accept(this); + + // Next, enter the global scope and walk all statements. + AutoLinkScope globalScope(&link_, unit_->globalScope()); + for (size_t i = 0; i < unit_->tree()->statements()->length(); i++) { + Statement *stmt = unit_->tree()->statements()->at(i); + stmt->accept(this); + } + } + + void visitVariableDeclaration(VariableDeclaration *node) { + // Bind the initializer before registering the declaration, so that we + // can error on bogus initializers (new x = x). + if (node->initialization()) + node->initialization()->accept(this); + + // If the node already has a symbol (meaning it was a global), then + // we don't have to do anything more. + if (node->sym()) + return; + + Local type(zone_, buildVariableType(node)); + if (!type) + return; + + VariableSymbol *sym = new (pool_) VariableSymbol(link_->scope(), node->name(), node->pos(), type); + node->setSymbol(sym); + + if (Symbol *other = link_->scope()->localLookup(sym->name())) { + // Report, but allow errors to continue. + cc_.reportError(sym->pos(), Message_RedeclaredName, + sym->name()->chars(), + other->pos().line, + other->pos().col); + return; + } + + link_->scope()->addSymbol(sym); + } + void visitEnumStatement(EnumStatement *node) { + } + void visitFunctionStatement(FunctionStatement *node) { + AutoLinkScope argScope(&link_, node->argScope()); + AutoLinkScope varScope(&link_, node->varScope()); + + // If the function is in the global scope, we've already computed its + // type. + if (node->sym()->scope()->kind() != Scope::GLOBAL) { + Local type(zone_, node->sym()->type()); + if (!fillFunctionType(type, node->signature())) + return; + } + + if (node->body()) + node->body()->accept(this); + } + void visitStructureStatement(StructureStatement *node) { + assert(false); + } + void visitFunctionTypeStatement(FunctionTypeStatement *node) { + assert(false); + } + + void visitNameProxy(NameProxy *proxy) { + // We could have already bound globals. + if (proxy->sym()) + return; + + Scope *scope = link_->scope(); + Symbol *sym = scope->lookup(proxy->name()); + if (!sym) + cc_.reportError(proxy->pos(), Message_IdentifierNotFound, proxy->name()->chars()); + proxy->bind(sym); + } + void visitAssignment(Assignment *node) { + node->lvalue()->accept(this); + node->expression()->accept(this); + } + void visitBinaryExpression(BinaryExpression *node) { + node->left()->accept(this); + node->right()->accept(this); + } + void visitReturnStatement(ReturnStatement *node) { + node->expression()->accept(this); + } + void visitForStatement(ForStatement *node) { + AutoLinkScope scope(&link_, node->scope()); + + if (node->initialization()) + node->initialization()->accept(this); + if (node->condition()) + node->condition()->accept(this); + if (node->update()) + node->update()->accept(this); + node->body()->accept(this); + } + void visitBlockStatement(BlockStatement *node) { + AutoLinkScope scope(&link_, node->scope()); + for (size_t i = 0; i < node->statements()->length(); i++) + node->statements()->at(i)->accept(this); + } + void visitIntegerLiteral(IntegerLiteral *node) { + } + void visitExpressionStatement(ExpressionStatement *node) { + node->expression()->accept(this); + } + void visitCallExpression(CallExpression *node) { + node->callee()->accept(this); + for (size_t i = 0; i < node->arguments()->length(); i++) + node->arguments()->at(i)->accept(this); + } + void visitIfStatement(IfStatement *node) { + node->condition()->accept(this); + node->ifTrue()->accept(this); + if (node->ifFalse()) + node->ifFalse()->accept(this); + } + void visitIndexExpression(IndexExpression *node) { + node->left()->accept(this); + node->right()->accept(this); + } + void visitFloatLiteral(FloatLiteral *node) { + } + void visitWhileStatement(WhileStatement *node) { + node->condition()->accept(this); + node->body()->accept(this); + } + void visitBreakStatement(BreakStatement *node) { + } + void visitContinueStatement(ContinueStatement *node) { + } + void visitStringLiteral(StringLiteral *node) { + } + void visitIncDecExpression(IncDecExpression *node) { + node->expression()->accept(this); + } + void visitUnaryExpression(UnaryExpression *node) { + if (node->tag()) + node->tag()->accept(this); + node->expression()->accept(this); + } + void visitTernaryExpression(TernaryExpression *node) { + node->condition()->accept(this); + node->left()->accept(this); + node->right()->accept(this); + } + void visitBooleanLiteral(BooleanLiteral *node) { + } + void visitFieldExpression(FieldExpression *node) { + node->left()->accept(this); + } + void visitSwitchStatement(SwitchStatement *node) { + node->expression()->accept(this); + node->defaultCase()->accept(this); + for (size_t i = 0; i < node->cases()->length(); i++) { + // We don't test case expressions because they are literals. + Case *c = node->cases()->at(i); + c->statement()->accept(this); + } + } + void visitArrayLiteral(ArrayLiteral *node) { + for (size_t i = 0; i < node->expressions()->length(); i++) + node->expressions()->at(i)->accept(this); + } + void visitImportStatement(ImportStatement *node) { + } + + private: + AutoLinkScope *link_; +}; + +void +ke::BindGlobals(Zone *zone, CompileContext &cc, TranslationUnit *unit) +{ + AutoEnterCompileContext enter(cc, unit); + + GlobalBuilder builder(zone, cc, unit); + Local globals(zone, builder.build()); + if (!globals || unit->failed()) + return; + + unit->module()->setGlobals(globals); +} + +void +ke::BindNamesAndTypes(Zone *zone, CompileContext &cc, TranslationUnit *unit) +{ + AutoEnterCompileContext enter(cc, unit); + NameBinder binder(zone, cc, unit); + binder.bind(); +} diff -r c4b1341297e5 -r ba85a47ee414 src/compiler/NameBinding.h --- a/src/compiler/NameBinding.h Mon Jun 10 00:46:29 2013 +0100 +++ b/src/compiler/NameBinding.h Mon Jun 10 01:04:43 2013 +0100 @@ -1,48 +1,48 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#ifndef _include_name_binding_h_ -#define _include_name_binding_h_ - -#include "../PoolAllocator.h" -#include "../Types.h" -#include "Scanner.h" - -namespace ke { - -class Zone; -class CompileContext; -struct TranslationUnit; - -// Creates symbols and types for names that are global or can be derived from -// a global. This phase also populates global scopes with lists of exported -// symbols. -void PopulateNamesAndTypes(Zone *zone, CompileContext &cc, TranslationUnit *unit); - -// Because of the way globals work in Pawn, we need a precursor step to -// actually enter global symbols into the global struct. They're already in -// the global symbol table, so we know they won't clash. This process also -// binds any remaining types on global variables. -void BindGlobals(Zone *zone, CompileContext &cc, TranslationUnit *unit); - -// Bind all outstanding names and finalize all unfinished types. -void BindNamesAndTypes(Zone *zone, CompileContext &cc, TranslationUnit *unit); - -} - -#endif // _include_name_binding_h_ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_name_binding_h_ +#define _include_name_binding_h_ + +#include "../PoolAllocator.h" +#include "../Types.h" +#include "Scanner.h" + +namespace ke { + +class Zone; +class CompileContext; +struct TranslationUnit; + +// Creates symbols and types for names that are global or can be derived from +// a global. This phase also populates global scopes with lists of exported +// symbols. +void PopulateNamesAndTypes(Zone *zone, CompileContext &cc, TranslationUnit *unit); + +// Because of the way globals work in Pawn, we need a precursor step to +// actually enter global symbols into the global struct. They're already in +// the global symbol table, so we know they won't clash. This process also +// binds any remaining types on global variables. +void BindGlobals(Zone *zone, CompileContext &cc, TranslationUnit *unit); + +// Bind all outstanding names and finalize all unfinished types. +void BindNamesAndTypes(Zone *zone, CompileContext &cc, TranslationUnit *unit); + +} + +#endif // _include_name_binding_h_ diff -r c4b1341297e5 -r ba85a47ee414 src/compiler/Parser.cpp --- a/src/compiler/Parser.cpp Mon Jun 10 00:46:29 2013 +0100 +++ b/src/compiler/Parser.cpp Mon Jun 10 01:04:43 2013 +0100 @@ -1,1787 +1,1787 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#include -#include "../Zone.h" -#include "../FixedArray.h" -#include "../Heap.h" -#include "../Strings.h" -#include "../TypeManager.h" -#include "Parser.h" -#include "../Heap-inl.h" - -using namespace ke; - -Parser::Parser(Zone *zone, CompileContext &cc, const char *stream, size_t length) - : zone_(zone), - cc_(cc), - pool_(zone_->pool()), - scanner_(cc, stream, length), - unbound_(NULL) -{ - unbound_ = new (pool_) PoolList(); - imports_ = new (pool_) PoolList(); -} - -bool -Parser::peek(TokenKind kind) -{ - return scanner_.peek() == kind; -} - -bool -Parser::match(TokenKind kind) -{ - if (!peek(kind)) - return false; - scanner_.scan(); - return true; -} - -bool -Parser::matchSemicolon() -{ - return expect(TOK_SEMICOLON); -} - -bool -Parser::expect(TokenKind kind) -{ - TokenKind got = scanner_.scan(); - if (got == kind) - return true; - cc_.reportError(scanner_.begin(), Message_WrongToken, TokenNames[kind], TokenNames[got]); - return false; -} - -bool -Parser::matchName(String **name, TokenKind kind) -{ - assert(kind == TOK_NAME || kind == TOK_LABEL); - *name = NULL; - if (!match(kind)) - return false; - *name = zone_->makeSymbol(scanner_.literal(), scanner_.literal_length()); - return true; -} - -String * -Parser::expectName() -{ - if (!expect(TOK_NAME)) - return NULL; - return zone_->makeSymbol(scanner_.literal(), scanner_.literal_length()); -} - -Expression * -Parser::primitive() -{ - TokenKind kind = scanner_.peek(); - switch (kind) { - case TOK_FLOAT_LITERAL: - { - scanner_.scan(); - double value = Scanner::ParseDouble(scanner_.literal()); - return new (pool_) FloatLiteral(scanner_.begin(), value); - } - - case TOK_HEX_LITERAL: - { - scanner_.scan(); - int value = strtol(scanner_.literal(), NULL, 16); - return new (pool_) IntegerLiteral(scanner_.begin(), value); - } - - case TOK_INTEGER_LITERAL: - { - scanner_.scan(); - int value = atoi(scanner_.literal()); - return new (pool_) IntegerLiteral(scanner_.begin(), value); - } - - case TOK_TRUE: - case TOK_FALSE: - scanner_.scan(); - return new (pool_) BooleanLiteral(scanner_.begin(), kind); - - case TOK_STRING_LITERAL: - { - scanner_.scan(); - Local string(zone_, zone_->makeSymbol(scanner_.literal(), scanner_.literal_length())); - return new (pool_) StringLiteral(scanner_.begin(), string); - } - - default: - { - if (kind != TOK_ERROR) - cc_.reportError(scanner_.begin(), Message_ExpectedExpression, TokenNames[kind]); - return NULL; - } - } -} - -Expression * -Parser::fixedArrayLiteral() -{ - SourcePosition pos = scanner_.begin(); - - ExpressionList *list = new (pool_) ExpressionList(); - do { - // { } is an illegal array literal. - if (list->length() > 0 && match(TOK_LBRACE)) - break; - - Expression *expr = NULL; - if (peek(TOK_LBRACE)) - expr = fixedArrayLiteral(); - else - expr = primitive(); - if (!expr) - return NULL; - - if (!list->append(expr)) - return NULL; - - if (!match(TOK_COMMA)) { - if (!expect(TOK_RBRACE)) - return NULL; - break; - } - } while (true); - - return new (pool_) ArrayLiteral(pos, TOK_LBRACE, list); -} - -Expression * -Parser::prefix() -{ - switch (scanner_.peek()) { - case TOK_LPAREN: - { - scanner_.scan(); - Expression *expr = expression(); - if (!expr) - return NULL; - if (!expect(TOK_RPAREN)) - return NULL; - return expr; - } - - case TOK_NAME: - { - Local name(zone_, expectName()); - if (!name) - return NULL; - return new (pool_) NameProxy(scanner_.begin(), name); - } - - default: - return primitive(); - } -} - -// Parses only old-style declarations. -bool -Parser::typeAndName(Expression **type, String **namep) -{ - if (match(TOK_LABEL)) { - // Old-style declaration: - // new X:type - Local name(zone_, zone_->makeSymbol(scanner_.literal(), scanner_.literal_length())); - if (!name) - return false; - - *type = new (pool_) NameProxy(scanner_.begin(), name); - *namep = expectName(); - return !!*namep; - } - - *type = NULL; - *namep = expectName(); - - return !!*namep; -} - -// Parses both old and new style declarations. -bool -Parser::fullTypeAndName(Expression **type, String **namep, DeclKind kind) -{ - // decl ::= newdecl | olddecl - // newdecl ::= Type Ident - // olddecl ::= Label? (Ident | "...") - // type ::= Ident - // label ::= Ident ":" - // - if (match(TOK_LABEL)) { - // We something of the form: - // Label Ident - // or: Label "..." - Local name(zone_, zone_->makeSymbol(scanner_.literal(), scanner_.literal_length())); - if (!name) - return false; - - *type = new (pool_) NameProxy(scanner_.begin(), name); - if (kind == DeclArg && match(TOK_ELLIPSES)) { - *namep = NULL; - } else { - if ((*namep = expectName()) == NULL) - return false; - } - return true; - } - - Local name(zone_, expectName()); - if (!name) - return false; - - if (peek(TOK_NAME) || (kind == DeclArg && peek(TOK_ELLIPSES))) { - // We got something of the form: - // Ident Ident - *type = new (pool_) NameProxy(scanner_.begin(), name); - if (peek(TOK_NAME)) { - if ((*namep = expectName()) == NULL) - return false; - } else { - *namep = NULL; - } - return true; - } - - // We got something of the form: - // Ident - *type = NULL; - *namep = name; - return true; -} - -Expression * -Parser::call(Expression *callee) -{ - ExpressionList *arguments = new (pool_) ExpressionList(); - - SourcePosition pos = scanner_.begin(); - expect(TOK_LPAREN); - - if (!match(TOK_RPAREN)) { - while (true) { - Expression *expr = expression(); - if (!expr) - return NULL; - - if (!arguments->append(expr)) - return NULL; - - if (!match(TOK_COMMA)) - break; - } - - if (!expect(TOK_RPAREN)) - return NULL; - } - - return new (pool_) CallExpression(pos, callee, arguments); -} - -Expression * -Parser::index(Expression *left) -{ - expect(TOK_LBRACKET); - - SourcePosition pos = scanner_.begin(); - Expression *expr = expression(); - if (!expr) - return NULL; - - if (!expect(TOK_RBRACKET)) - return NULL; - - return new (pool_) IndexExpression(pos, left, expr); -} - -Expression * -Parser::field(Expression *left) -{ - expect(TOK_DOT); - - SourcePosition pos = scanner_.begin(); - Local name(zone_, expectName()); - if (!name) - return NULL; - - return new (pool_) FieldExpression(pos, left, name); -} - -Expression * -Parser::primary() -{ - Expression *expr = prefix(); - if (!expr) - return NULL; - - for (;;) { - switch (scanner_.peek()) { - case TOK_LPAREN: - if ((expr = call(expr)) == NULL) - return NULL; - break; - - case TOK_LBRACKET: - if ((expr = index(expr)) == NULL) - return NULL; - break; - - case TOK_DOT: - if ((expr = field(expr)) == NULL) - return NULL; - break; - - default: - return expr; - } - } -} - -Expression * -Parser::unary() -{ - TokenKind token = scanner_.peek(); - - SourcePosition pos = scanner_.begin(); - switch (token) { - case TOK_INCREMENT: - case TOK_DECREMENT: - { - scanner_.scan(); - Expression *expr = unary(); - if (!expr) - return NULL; - return new (pool_) IncDecExpression(pos, token, expr, false); - } - - case TOK_MINUS: - case TOK_NOT: - case TOK_TILDE: - { - scanner_.scan(); - Expression *expr = unary(); - if (!expr) - return NULL; - return new (pool_) UnaryExpression(pos, token, expr); - } - - case TOK_SIZEOF: - { - scanner_.scan(); - if (!expect(TOK_LPAREN)) - return NULL; - Expression *expr = unary(); - if (!expr) - return NULL; - if (!expect(TOK_RPAREN)) - return NULL; - return new (pool_) UnaryExpression(pos, token, expr); - } - - case TOK_LABEL: - { - scanner_.scan(); - Local tag(zone_, zone_->makeSymbol(scanner_.literal(), scanner_.literal_length())); - if (!tag) - return NULL; - - NameProxy *proxy = new (pool_) NameProxy(scanner_.begin(), tag); - - Expression *expr = unary(); - if (!expr) - return NULL; - - return new (pool_) UnaryExpression(pos, TOK_LABEL, expr, proxy); - } - - default: - break; - } - - Expression *expr = primary(); - - token = scanner_.peek(); - if (token == TOK_INCREMENT || token == TOK_DECREMENT) { - scanner_.scan(); - return new (pool_) IncDecExpression(pos, token, expr, true); - } - - return expr; -} - -Expression * -Parser::multiplication() -{ - Expression *left = unary(); - do { - if (!match(TOK_SLASH) && !match(TOK_STAR) && !match(TOK_PERCENT)) - break; - SourcePosition pos = scanner_.begin(); - TokenKind kind = scanner_.last(); - Expression *right = unary(); - if (!right) - return NULL; - left = new (pool_) BinaryExpression(pos, kind, left, right); - } while (left); - return left; -} - -Expression * -Parser::addition() -{ - Expression *left = multiplication(); - while (left) { - if (!match(TOK_PLUS) && !match(TOK_MINUS)) - break; - SourcePosition pos = scanner_.begin(); - TokenKind kind = scanner_.last(); - Expression *right = multiplication(); - if (!right) - return NULL; - left = new (pool_) BinaryExpression(pos, kind, left, right); - } - return left; -} - -Expression * -Parser::shift() -{ - Expression *left = addition(); - while (left) { - if (!match(TOK_SHL) && !match(TOK_SHR) && !match(TOK_USHR)) - break; - SourcePosition pos = scanner_.begin(); - TokenKind kind = scanner_.last(); - Expression *right = addition(); - if (!right) - return NULL; - left = new (pool_) BinaryExpression(pos, kind, left, right); - } - return left; -} - -Expression * -Parser::bitand_() -{ - Expression *left = shift(); - while (left) { - if (!match(TOK_BITAND)) - break; - SourcePosition pos = scanner_.begin(); - Expression *right = shift(); - if (!right) - return NULL; - left = new (pool_) BinaryExpression(pos, TOK_BITAND, left, right); - } - return left; -} - -Expression * -Parser::bitxor() -{ - Expression *left = bitand_(); - while (left) { - if (!match(TOK_BITXOR)) - break; - SourcePosition pos = scanner_.begin(); - Expression *right = shift(); - if (!right) - return NULL; - left = new (pool_) BinaryExpression(pos, TOK_BITXOR, left, right); - } - return left; -} - -Expression * -Parser::bitor_() -{ - Expression *left = bitxor(); - while (left) { - if (!match(TOK_BITOR)) - break; - SourcePosition pos = scanner_.begin(); - Expression *right = bitxor(); - if (!right) - return NULL; - left = new (pool_) BinaryExpression(pos, TOK_BITOR, left, right); - } - return left; -} - -Expression * -Parser::relational() -{ - Expression *left = bitor_(); - while (left) { - TokenKind kind = scanner_.peek(); - if (kind < TOK_LT || kind > TOK_GE) - break; - scanner_.scan(); - SourcePosition pos = scanner_.begin(); - Expression *right = shift(); - if (!right) - return NULL; - left = new (pool_) BinaryExpression(pos, kind, left, right); - } - return left; -} - -Expression * -Parser::equals() -{ - Expression *left = relational(); - while (left) { - if (!match(TOK_EQUALS) && !match(TOK_NOTEQUALS)) - break; - TokenKind kind = scanner_.last(); - SourcePosition pos = scanner_.begin(); - Expression *right = relational(); - if (!right) - return NULL; - left = new (pool_) BinaryExpression(pos, kind, left, right); - } - return left; -} - -Expression * -Parser::and_() -{ - Expression *left = equals(); - while (left) { - if (!match(TOK_AND)) - break; - SourcePosition pos = scanner_.begin(); - Expression *right = equals(); - if (!right) - return NULL; - left = new (pool_) BinaryExpression(pos, TOK_AND, left, right); - } - return left; -} - -Expression * -Parser::or_() -{ - Expression *left = and_(); - while (left) { - if (!match(TOK_OR)) - break; - SourcePosition pos = scanner_.begin(); - Expression *right = and_(); - if (!right) - return NULL; - left = new (pool_) BinaryExpression(pos, TOK_OR, left, right); - } - return left; -} - -Expression * -Parser::ternary() -{ - Expression *cond = or_(); - if (!cond) - return NULL; - - if (!match(TOK_QMARK)) - return cond; - - Expression *left; - SourcePosition pos = scanner_.begin(); - AutoAllowTags disableTags(scanner_); - { - if ((left = expression()) == NULL) - return NULL; - } - - if (!expect(TOK_COLON)) - return NULL; - - Expression *right = expression(); - if (!right) - return NULL; - - return new (pool_) TernaryExpression(pos, cond, left, right); -} - -Expression * -Parser::assignment() -{ - Expression *left = ternary(); - while (left) { - TokenKind token = scanner_.peek(); - if (token != TOK_ASSIGN && (token < TOK_ASSIGN_ADD || token > TOK_ASSIGN_SHL)) - break; - scanner_.scan(); - SourcePosition pos = scanner_.begin(); - Expression *expr = ternary(); - if (!expr) - return NULL; - - left = new (pool_) Assignment(pos, token, left, expr); - } - return left; -} - -Expression * -Parser::expression() -{ - return assignment(); -} - -Statement * -Parser::while_() -{ - // while ::= "while" "(" expr ")" statement - SourcePosition pos = scanner_.begin(); - scanner_.scan(); - - if (!expect(TOK_LPAREN)) - return NULL; - - Expression *condition = expression(); - if (!condition) - return NULL; - - if (!expect(TOK_RPAREN)) - return NULL; - - Statement *body; - if (scanner_.peek() == TOK_LBRACE) - body = block(); - else - body = localStatement(); - if (!body) - return NULL; - - return new (pool_) WhileStatement(pos, TOK_WHILE, condition, body); -} - -Statement * -Parser::do_() -{ - // do ::= "do" block "while" "(" expr ")" - SourcePosition pos = scanner_.begin(); - scanner_.scan(); - - Statement *body = block(); - if (!body) - return NULL; - - if (!expect(TOK_WHILE)) - return NULL; - - if (!expect(TOK_LPAREN)) - return NULL; - Expression *condition = expression(); - if (!condition) - return NULL; - if (!expect(TOK_RPAREN)) - return NULL; - - return new (pool_) WhileStatement(pos, TOK_DO, condition, body); -} - -Statement * -Parser::switch_() -{ - // switch ::= "switch" "(" expr ")" "{" case* defaultcase? "}" - // case ::= "case" casevals ":" statement - // defaultcase ::= "default" ":" statement - // - expect(TOK_SWITCH); - - SourcePosition pos = scanner_.begin(); - - if (!expect(TOK_LPAREN)) - return NULL; - Expression *expr = expression(); - if (!expr) - return NULL; - if (!expect(TOK_RPAREN)) - return NULL; - - if (!expect(TOK_LBRACE)) - return NULL; - - SourcePosition defaultPos; - PoolList *cases = new (pool_) PoolList(); - - Statement *defaultCase = NULL; - - while (!peek(TOK_RBRACE)) { - Expression *expr = NULL; - ExpressionList *others = NULL; - if (match(TOK_DEFAULT)) { - if (defaultCase) { - cc_.reportError(scanner_.begin(), Message_OneDefaultPerSwitch); - return NULL; - } - - defaultPos = scanner_.begin(); - } else { - if (defaultCase) { - cc_.reportError(defaultPos, Message_DefaultMustBeLastCase); - return NULL; - } - - if (!expect(TOK_CASE)) - return NULL; - - // A limitation in the grammar is that |case :| will be - // detected as a label. - AutoAllowTags disableTags(scanner_); - - if ((expr = expression()) == NULL) - return NULL; - - if (peek(TOK_COMMA)) { - others = new (pool_) ExpressionList(); - while (match(TOK_COMMA)) { - Expression *other = expression(); - if (!other) - return NULL; - if (!others->append(other)) - return NULL; - } - } - } - - if (!expect(TOK_COLON)) - return NULL; - - Statement *statement = localStatement(); - if (!statement) - return NULL; - - if (!peek(TOK_CASE) && - !peek(TOK_DEFAULT) && - !peek(TOK_RBRACE)) - { - cc_.reportError(scanner_.begin(), Message_SingleStatementPerCase); - return NULL; - } - - if (expr) { - Case *caze = new (pool_) Case(expr, others, statement); - if (!cases->append(caze)) - return NULL; - } else { - defaultCase = statement; - } - } - - if (!expect(TOK_RBRACE)) - return NULL; - - return new (pool_) SwitchStatement(pos, expr, cases, defaultCase); -} - -Statement * -Parser::forStatement() -{ - // for ::= "for" "(" forinit? ";" forcond? ";" forstep ")" statement - // forint ::= "new" vardecl | - // exprstmt - // forcond ::= expr - // forstep ::= exprstmt - // - SourcePosition pos = scanner_.begin(); - scanner_.scan(); - if (!expect(TOK_LPAREN)) - return NULL; - - Statement *decl = NULL; - if (!match(TOK_SEMICOLON)) { - if (match(TOK_NEW)) { - if ((decl = vars(TypeQual_None)) == NULL) - return NULL; - } else { - if ((decl = expressionStatement()) == NULL) - return NULL; - } - if (!expect(TOK_SEMICOLON)) - return NULL; - } - - Expression *condition = NULL; - if (!match(TOK_SEMICOLON)) { - if ((condition = expression()) == NULL) - return NULL; - if (!expect(TOK_SEMICOLON)) - return NULL; - } - - Statement *update = NULL; - if (!match(TOK_RPAREN)) { - if ((update = expressionStatement()) == NULL) - return NULL; - if (!expect(TOK_RPAREN)) - return NULL; - } - - Statement *body; - if (scanner_.peek() == TOK_LBRACE) - body = block(); - else - body = localStatement(); - if (!body) - return NULL; - - return new (pool_) ForStatement(pos, decl, condition, update, body); -} - -ExpressionList * -Parser::dimensions() -{ - // dimensions ::= ("[" expr? "]")* - - ExpressionList *postDimensions = new (pool_) ExpressionList(); - while (match(TOK_LBRACKET)) { - Expression *dim = NULL; - if (!match(TOK_RBRACKET)) { - if ((dim = expression()) == NULL) - return NULL; - if (!expect(TOK_RBRACKET)) - return NULL; - } - - if (!postDimensions->append(dim)) - return NULL; - } - return postDimensions; -} - -VariableDeclaration * -Parser::var(TypeQualifiers qual) -{ - // vardecl ::= decl ("=" expr)? - - Expression *type; - Local name(zone_); - - if (!typeAndName(&type, name.ref())) - return NULL; - - ExpressionList *dims = NULL; - if (peek(TOK_LBRACKET)) { - if ((dims = dimensions()) == NULL) - return NULL;; - } - - SourcePosition pos = scanner_.begin(); - - Expression *expr = NULL; - if (match(TOK_ASSIGN)) { - switch (scanner_.peek()) { - // Note: we don't support [] literals yet. - case TOK_LBRACE: - scanner_.scan(); - if ((expr = fixedArrayLiteral()) == NULL) - return NULL; - break; - - default: - if ((expr = expression()) == NULL) - return NULL; - break; - } - } - - return new (pool_) VariableDeclaration(pos, qual, name, type, dims, expr); -} - -Statement * -Parser::vars(TypeQualifiers qual) -{ - // localvars ::= ("new" | "const" | "decl") vardecl ("," vardecl)* - // globalvars ::= ("public" | "const" | "new" | "public" "const") vardecl - VariableDeclaration *decl = var(qual); - if (!decl) - return NULL; - - if (!peek(TOK_COMMA)) - return decl; - - StatementList *statements = new (pool_) StatementList(); - if (!statements->append(decl)) - return NULL; - - while (match(TOK_COMMA)) { - Statement *decl = var(qual); - if (!decl) - return NULL; - if (!statements->append(decl)) - return NULL; - } - - // Create a fake-o block with no scope to create a statement list. - return new (pool_) BlockStatement(decl->pos(), statements, TOK_NEW); -} - -Statement * -Parser::returnStatement() -{ - // return ::= "return" term | - // "return" "expr" - - SourcePosition pos = scanner_.begin(); - if (!expect(TOK_RETURN)) - return NULL; - - Expression *expr = NULL; - if (scanner_.peek() != TOK_SEMICOLON) { - if ((expr = expression()) == NULL) - return NULL; - - // We only care about non-void returns when determining whether a - // tagless function is non-void. - encounteredReturn_ = true; - } - - return new (pool_) ReturnStatement(pos, expr); -} - -Statement * -Parser::expressionStatement() -{ - // exprstmt ::= expr - Expression *left = assignment(); - if (!left) - return NULL; - - return new (pool_) ExpressionStatement(left); -} - -Statement * -Parser::block() -{ - // block ::= "{" statement* "}" - - if (!expect(TOK_LBRACE)) - return NULL; - - SourcePosition pos = scanner_.begin(); - - StatementList *statements = new (pool_) StatementList(); - while (!match(TOK_RBRACE)) { - Statement *statement = localStatement(); - if (statement == NULL) - return NULL; - if (!statements->append(statement)) - return NULL; - } - - BlockStatement *block = new (pool_) BlockStatement(pos, statements, TOK_LBRACE); - return block; -} - -Statement * -Parser::if_() -{ - // if ::= "if" "(" expr ")" statement elseif* else? - // elseif ::= "elseif" "(" expr ")" statement - // else ::= "else" statement - expect(TOK_IF); - - SourcePosition pos = scanner_.begin(); - if (!expect(TOK_LPAREN)) - return NULL; - - Expression *cond = expression(); - if (!cond) - return NULL; - - if (!expect(TOK_RPAREN)) - return NULL; - - Statement *ifTrue = localStatement(); - if (!ifTrue) - return NULL; - - IfStatement *outer = new (pool_) IfStatement(pos, cond, ifTrue); - - IfStatement *last = outer; - while (match(TOK_ELSE)) { - if (!match(TOK_IF)) { - Statement *ifFalse = localStatement(); - if (!ifFalse) - return NULL; - - last->setIfFalse(ifFalse); - break; - } - - SourcePosition pos = scanner_.begin(); - if (!expect(TOK_LPAREN)) - return NULL; - - Expression *otherCond = expression(); - if (!otherCond) - return NULL; - - if (!expect(TOK_RPAREN)) - return NULL; - - Statement *otherIfTrue = localStatement(); - if (!otherIfTrue) - return NULL; - - IfStatement *inner = new (pool_) IfStatement(pos, otherCond, otherIfTrue); - last->setIfFalse(inner); - last = inner; - } - - return outer; -} - -Statement * -Parser::localStatement() -{ - // statement ::= stmt term - // stmt ::= do | for | if | while | struct | enum | - // localvars | return | switch | break | continue - - Statement *statement = NULL; - - // Statements which must be followed by a semicolon will break out of the - // switch. If they may end in BlockStatements, they'll immediately return. - - TokenKind kind = scanner_.peek(); - switch (kind) { - case TOK_CONST: - case TOK_NEW: - { - TypeQualifiers quals = (kind == TOK_CONST) - ? TypeQual_Const - : TypeQual_None; - scanner_.scan(); - if ((statement = vars(quals)) == NULL) - return NULL; - break; - } - - case TOK_FOR: - return forStatement(); - - case TOK_WHILE: - return while_(); - - case TOK_BREAK: - scanner_.scan(); - statement = new (pool_) BreakStatement(scanner_.begin()); - break; - - case TOK_CONTINUE: - scanner_.scan(); - statement = new (pool_) ContinueStatement(scanner_.begin()); - break; - - case TOK_DO: - if ((statement = do_()) == NULL) - return NULL; - break; - - case TOK_RETURN: - if ((statement = returnStatement()) == NULL) - return NULL; - break; - - case TOK_ENUM: - if ((statement = enum_(TOK_NONE)) == NULL) - return NULL; - break; - - case TOK_STRUCT: - if ((statement = struct_()) == NULL) - return NULL; - break; - - case TOK_SWITCH: - return switch_(); - - case TOK_IF: - return if_(); - - case TOK_LBRACE: - return block(); - - default: - break; - } - - if (!statement) { - if ((statement = expressionStatement()) == NULL) - return NULL; - } - - if (!expect(TOK_SEMICOLON)) - return NULL; - - return statement; -} - -Statement * -Parser::struct_() -{ - // struct ::= "struct" name { struct_fields } - // struct_fields ::= struct_field | - // struct_field struct_fields - // struct_field ::= label? name term - expect(TOK_STRUCT); - - Local name(zone_, expectName()); - if (!name) - return NULL; - - SourcePosition pos = scanner_.begin(); - - if (!expect(TOK_LBRACE)) - return NULL; - - FieldList *fields = new (pool_) FieldList(); - - while (!peek(TOK_RBRACE)) { - Expression *type; - Local fieldName(zone_); - - if (!fullTypeAndName(&type, fieldName.ref(), DeclField)) - return NULL; - - SourcePosition pos = scanner_.begin(); - - if (!matchSemicolon()) - return NULL; - - StructureStatement::Field *field = - new (pool_) StructureStatement::Field(pos, type, fieldName); - if (!field || !fields->append(field)) - return NULL; - } - - if (!expect(TOK_RBRACE)) - return NULL; - - if (fields->empty()) { - cc_.reportError(pos, Message_StructCantBeEmpty); - return NULL; - } - - return new (pool_) StructureStatement(pos, name, fields); -} - -Statement * -Parser::enum_(TokenKind kind) -{ - // enum ::= "enum" name? { enum_members? } - // enum_members ::= enum_member ","? | - // enum_member "," enum_members - // enum_member ::= ident ("=" constexpr)? - expect(TOK_ENUM); - - SourcePosition pos = scanner_.begin(); - - Local name(zone_); - if (matchName(name.ref()) && !name) - return NULL; - - EnumStatement::EntryList *entries = new (pool_) EnumStatement::EntryList(); - - if (!expect(TOK_LBRACE)) - return NULL; - - do { - if (scanner_.peek() == TOK_RBRACE) - break; - - SourcePosition pos = scanner_.begin(); - Local name(zone_, expectName()); - if (!name) - return NULL; - - Expression *expr = NULL; - if (match(TOK_ASSIGN)) { - if ((expr = expression()) == NULL) - return NULL; - } - - if (!entries->append(EnumStatement::Entry(name, expr, pos))) - return NULL; - } while (match(TOK_COMMA)); - if (!expect(TOK_RBRACE)) - return NULL; - - return new (pool_) EnumStatement(pos, kind, name, entries); -} - -template -static inline ScopedRoot * -NewScopedRoot(PoolAllocator &pool, Handle obj) -{ - return new (pool) ScopedRoot(obj); -} - -ImportStatement * -Parser::import(TokenKind kind) -{ - SourcePosition pos = scanner_.begin(); - - // import ::= "from" import_path "import" import_names - // | "import" import_path - // | "import" import_path ".*" - // import_path ::= name - // | name "." import_path - // import_names ::= "*" - // | import_name_list - // import_name_list ::= name - // | name "," import_name_list - - Local name(zone_, expectName()); - if (!name) - return NULL; - - PathComponent *root = new (pool_) PathComponent(NewScopedRoot(pool_, name), NULL); - PathComponent *current = root; - - // NULL namelist means that we do not import any names. An empty namelist - // means we import all names. - NameList *names = NULL; - - while (match(TOK_DOT)) { - if (kind == TOK_IMPORT && match(TOK_STAR)) { - names = new (pool_) NameList(); - break; - } - - if ((name = expectName()) == NULL) - return NULL; - - current->next = new (pool_) PathComponent(NewScopedRoot(pool_, name), NULL); - current = current->next; - } - - if (kind == TOK_FROM) { - if (!expect(TOK_IMPORT)) - return NULL; - - names = new (pool_) NameList(); - - if (!match(TOK_STAR)) { - if (!matchName(name.address())) { - cc_.reportError(scanner_.begin(), Message_BadImportFrom); - return NULL; - } - - if (!name || !names->append(NewScopedRoot(pool_, name))) - return NULL; - - while (match(TOK_COMMA)) { - if ((name = expectName()) == NULL) - return NULL; - if (!names->append(NewScopedRoot(pool_, name))) - return NULL; - } - } - } - - return new (pool_) ImportStatement(pos, root, names); -} - -Statement * -Parser::function(TokenKind token) -{ - Expression *returnType; - Local name(zone_); - - if (!fullTypeAndName(&returnType, name.ref(), DeclFun)) - return NULL; - - SourcePosition pos = scanner_.begin(); - ParameterList *parameters = new (pool_) ParameterList(); - - if (!expect(TOK_LPAREN)) - return NULL; - - bool variadic = false; - - if (!match(TOK_RPAREN)) { - while (true) { - TypeQualifiers quals = TypeQual_None; - if (match(TOK_CONST)) - quals = TypeQualifiers(quals | TypeQual_Const); - - bool ref = match(TOK_AMPERSAND); - - SourcePosition begin = scanner_.begin(); - Expression *type; - Local name(zone_); - if (!fullTypeAndName(&type, name.ref(), DeclArg)) - return NULL; - - if (!name) { - if ((quals & TypeQual_Const) || ref) { - cc_.reportError(begin, Message_InvalidVarArgs); - return NULL; - } - - if (token != TOK_NATIVE) { - cc_.reportError(begin, Message_VarArgsOutsideNative); - return NULL; - } - - variadic = true; - break; - } - - ExpressionList *dims = NULL; - if (peek(TOK_LBRACKET)) { - if ((dims = dimensions()) == NULL) - return NULL; - } - - Expression *expr = NULL; - if (match(TOK_ASSIGN)) { - if ((expr = expression()) == NULL) - return NULL; - } - - Parameter *parameter = new (pool_) Parameter(name, type, ref, quals, dims, expr, begin); - if (!parameters->append(parameter)) - return NULL; - - if (!match(TOK_COMMA)) - break; - } - if (!expect(TOK_RPAREN)) - return NULL; - } - - encounteredReturn_ = false; - - Statement *body = NULL; - if (token != TOK_FORWARD && token != TOK_NATIVE && token != TOK_TYPEDEF) { - body = block(); - if (!body) - return NULL; - } - - FunctionSignature signature(returnType, parameters, variadic); - if (token == TOK_TYPEDEF) - return new (pool_) FunctionTypeStatement(pos, name, signature); - - return new (pool_) FunctionStatement(pos, name, token, body, signature, encounteredReturn_); -} - -static inline bool -DeclareIntrinsicType(Zone *zone, Scope *scope, const char *name, Handle type) -{ - Local tag(zone, zone->makeSymbol(name)); - if (!tag) - return false; - - assert(false); - return false; //return !!scope->declareType(tag, type); -} - -static inline bool -DeclareIntrinsicType(Zone *zone, Scope *scope, const char *name, PrimitiveType type) -{ - Local primitive(zone, zone->types()->getPrimitive(type)); - assert(primitive); - return DeclareIntrinsicType(zone, scope, name, primitive); -} - -ParseTree * -Parser::parse() -{ - StatementList *statements = new (pool_) StatementList(); - - for (;;) { - Statement *statement = NULL; - - TokenKind kind = scanner_.peek(); - switch (kind) { - case TOK_ERROR: - return NULL; - - case TOK_EOF: - break; - - case TOK_NAME: - if ((statement = function(TOK_NAME)) == NULL) - return NULL; - break; - - case TOK_FROM: - case TOK_IMPORT: - { - scanner_.scan(); - if (statements->length()) - cc_.reportError(scanner_.begin(), Message_ImportsMustBeFirst); - ImportStatement *stmt = import(kind); - if (!stmt) - return NULL; - if (!imports_->append(stmt)) - return NULL; - if (!matchSemicolon()) - return NULL; - continue; - } - - case TOK_FORWARD: - case TOK_PUBLIC: - case TOK_NATIVE: - case TOK_STOCK: - case TOK_LABEL: - case TOK_TYPEDEF: - { - if (kind != TOK_LABEL) - scanner_.scan(); - if (kind == TOK_PUBLIC && peek(TOK_CONST)) { - if ((statement = var(TypeQual_Const)) == NULL) - return NULL; - if (!matchSemicolon()) - return NULL; - } else if (kind == TOK_PUBLIC && peek(TOK_ENUM)) { - if ((statement = enum_(kind)) == NULL) - return NULL; - if (!matchSemicolon()) - return NULL; - } else { - if ((statement = function(kind)) == NULL) - return NULL; - if ((kind == TOK_FORWARD || kind == TOK_NATIVE || kind == TOK_TYPEDEF) && !matchSemicolon()) - return NULL; - } - break; - } - - case TOK_CONST: - case TOK_NEW: - { - TypeQualifiers quals = (kind == TOK_CONST) ? TypeQual_Const : TypeQual_None; - // Note: at global scope, we don't accept comma-delimited - // declarations yet, to see if we can get away with forcing - // a nicer top-level style. - scanner_.scan(); - if ((statement = var(quals)) == NULL) - return NULL; - if (!matchSemicolon()) - return NULL; - break; - } - - case TOK_ENUM: - if ((statement = enum_(TOK_NONE)) == NULL) - return NULL; - if (!matchSemicolon()) - return NULL; - break; - - case TOK_STRUCT: - if ((statement = struct_()) == NULL) - return NULL; - if (!matchSemicolon()) - return NULL; - break; - - default: - cc_.reportError(scanner_.begin(), Message_ExpectedGlobal); - return NULL; - } - - if (!statement) - break; - - if (!statements->append(statement)) - return NULL; - } - - return new (pool_) ParseTree(statements, unbound_, imports_); -} - -class AstPrinter : public AstVisitor -{ - FILE *fp_; - size_t level_; - - private: - void prefix() { - for (size_t i = 0; i < level_; i++) - fprintf(fp_, " "); - } - void indent() { - level_++; - } - void unindent() { - level_--; - } - - public: - AstPrinter(FILE *fp) - : fp_(fp), - level_(0) - { - } - - void dump(const FunctionSignature &sig) { - prefix(); - fprintf(fp_, "()\n"); - } - void visitNameProxy(NameProxy *name) { - prefix(); - fprintf(fp_, "[ NameProxy (%s)\n", name->name()->chars()); - } - void visitCallExpression(CallExpression *node) { - prefix(); - fprintf(fp_, "[ CallExpression\n"); - indent(); - node->callee()->accept(this); - for (size_t i = 0; i < node->arguments()->length(); i++) - node->arguments()->at(i)->accept(this); - unindent(); - } - void visitFunctionTypeStatement(FunctionTypeStatement *node) { - prefix(); - fprintf(fp_, "[ FunctionTypeStatement (%s)\n", node->name()->chars()); - indent(); - dump(node->signature()); - unindent(); - } - void visitFunctionStatement(FunctionStatement *node) { - prefix(); - fprintf(fp_, "[ FunctionStatement (%s)\n", node->name()->chars()); - indent(); - dump(node->signature()); - if (node->body()) - node->body()->accept(this); - unindent(); - } - void visitExpressionStatement(ExpressionStatement *node) { - prefix(); - fprintf(fp_, "[ ExpressionStatement\n"); - indent(); - node->expression()->accept(this); - unindent(); - } - void visitAssignment(Assignment *node) { - prefix(); - fprintf(fp_, "[ Assignment\n"); - indent(); - node->lvalue()->accept(this); - node->expression()->accept(this); - unindent(); - } - void visitTernaryExpression(TernaryExpression *node) { - prefix(); - fprintf(fp_, "[ TernaryExpression\n"); - indent(); - node->condition()->accept(this); - node->left()->accept(this); - node->right()->accept(this); - unindent(); - } - void visitBinaryExpression(BinaryExpression *node) { - prefix(); - fprintf(fp_, "[ BinaryExpression (%s)\n", TokenNames[node->token()]); - indent(); - node->left()->accept(this); - node->right()->accept(this); - unindent(); - } - void visitUnaryExpression(UnaryExpression *node) { - prefix(); - fprintf(fp_, "[ UnaryExpression (%s)\n", TokenNames[node->token()]); - indent(); - node->expression()->accept(this); - unindent(); - } - void visitReturnStatement(ReturnStatement *node) { - prefix(); - fprintf(fp_, "[ ReturnStatement\n"); - indent(); - if (node->expression()) - node->expression()->accept(this); - unindent(); - } - void visitForStatement(ForStatement *node) { - prefix(); - fprintf(fp_, "[ ForStatement\n"); - indent(); - if (node->initialization()) - node->initialization()->accept(this); - if (node->condition()) - node->condition()->accept(this); - if (node->update()) - node->update()->accept(this); - node->body()->accept(this); - unindent(); - } - void visitBlockStatement(BlockStatement *node) { - prefix(); - fprintf(fp_, "[ BlockStatement\n"); - indent(); - for (size_t i = 0; i < node->statements()->length(); i++) - node->statements()->at(i)->accept(this); - unindent(); - } - void visitVariableDeclaration(VariableDeclaration *node) { - prefix(); - fprintf(fp_, "[ VariableDeclaration (%s)\n", node->name()->chars()); - indent(); - if (node->initialization()) - node->initialization()->accept(this); - unindent(); - } - void visitIntegerLiteral(IntegerLiteral *node) { - prefix(); - fprintf(fp_, "[ IntegerLiteral (%d)\n", node->value()); - } - void visitBooleanLiteral(BooleanLiteral *node) { - prefix(); - fprintf(fp_, "[ BooleanLiteral (%s)\n", - (node->token() == TOK_TRUE) ? "true" : "false"); - } - void visitFloatLiteral(FloatLiteral *node) { - prefix(); - fprintf(fp_, "[ FloatLiteral (%f)\n", node->value()); - } - void visitIfStatement(IfStatement *node) { - prefix(); - fprintf(fp_, "[ IfStatement\n"); - indent(); - node->ifTrue()->accept(this); - if (node->ifFalse()) - node->ifFalse()->accept(this); - unindent(); - } - void visitIndexExpression(IndexExpression *node) { - prefix(); - fprintf(fp_, "[ IndexExpression\n"); - indent(); - node->left()->accept(this); - node->right()->accept(this); - unindent(); - } - void visitEnumStatement(EnumStatement *node) { - prefix(); - fprintf(fp_, "[ EnumStatement (%s)\n", node->name() ? node->name()->chars() : ""); - indent(); - for (size_t i = 0; i < node->entries()->length(); i++) { - prefix(); - fprintf(fp_, "%s =\n", node->entries()->at(i).name->chars()); - if (node->entries()->at(i).expr) { - indent(); - node->entries()->at(i).expr->accept(this); - unindent(); - } - } - unindent(); - } - void visitWhileStatement(WhileStatement *node) { - prefix(); - fprintf(fp_, "[ WhileStatement (%s)\n", - (node->token() == TOK_DO) ? "do" : "while"); - indent(); - if (node->token() == TOK_DO) { - node->condition()->accept(this); - node->body()->accept(this); - } else { - node->condition()->accept(this); - node->body()->accept(this); - } - unindent(); - } - void visitBreakStatement(BreakStatement *node) { - prefix(); - fprintf(fp_, "[ BreakStatement\n"); - } - void visitContinueStatement(ContinueStatement *node) { - prefix(); - fprintf(fp_, "[ ContinueStatement\n"); - } - void visitStringLiteral(StringLiteral *node) { - prefix(); - fprintf(fp_, "[ StringLiteral\n"); - } - void visitIncDecExpression(IncDecExpression *node) { - prefix(); - fprintf(fp_, "[ IncDecExpression (postfix=%d)\n", node->postfix()); - indent(); - node->expression()->accept(this); - unindent(); - } - void visitStructureStatement(StructureStatement *node) { - prefix(); - fprintf(fp_, "[ StructureStatement %s\n", node->name()->chars()); - } - void visitFieldExpression(FieldExpression *node) { - prefix(); - fprintf(fp_, "[ FieldExpression %s\n", node->field()->chars()); - indent(); - node->left()->accept(this); - unindent(); - } - void visitSwitchStatement(SwitchStatement *node) { - prefix(); - fprintf(fp_, "[ SwitchStatement\n"); - indent(); - node->expression()->accept(this); - for (size_t i = 0; i < node->cases()->length(); i++) { - Case *c = node->cases()->at(i); - c->expression()->accept(this); - if (c->others()) { - for (size_t j = 0; j < c->others()->length(); j++) - c->others()->at(j)->accept(this); - } - indent(); - c->statement()->accept(this); - unindent(); - } - if (node->defaultCase()) - node->defaultCase()->accept(this); - unindent(); - } - void visitArrayLiteral(ArrayLiteral *node) { - prefix(); - fprintf(fp_, "[ ArrayLiteral\n"); - indent(); - for (size_t i = 0; i < node->expressions()->length(); i++) { - Expression *expr = node->expressions()->at(i); - indent(); - expr->accept(this); - unindent(); - } - } - void visitImportStatement(ImportStatement *node) { - prefix(); - fprintf(fp_, "[ ImportStatement\n"); - } -}; - -void -ParseTree::dump(FILE *fp) -{ - AstPrinter printer(fp); - - for (size_t i = 0; i < statements_->length(); i++) - statements_->at(i)->accept(&printer); -} +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#include +#include "../Zone.h" +#include "../FixedArray.h" +#include "../Heap.h" +#include "../Strings.h" +#include "../TypeManager.h" +#include "Parser.h" +#include "../Heap-inl.h" + +using namespace ke; + +Parser::Parser(Zone *zone, CompileContext &cc, const char *stream, size_t length) + : zone_(zone), + cc_(cc), + pool_(zone_->pool()), + scanner_(cc, stream, length), + unbound_(NULL) +{ + unbound_ = new (pool_) PoolList(); + imports_ = new (pool_) PoolList(); +} + +bool +Parser::peek(TokenKind kind) +{ + return scanner_.peek() == kind; +} + +bool +Parser::match(TokenKind kind) +{ + if (!peek(kind)) + return false; + scanner_.scan(); + return true; +} + +bool +Parser::matchSemicolon() +{ + return expect(TOK_SEMICOLON); +} + +bool +Parser::expect(TokenKind kind) +{ + TokenKind got = scanner_.scan(); + if (got == kind) + return true; + cc_.reportError(scanner_.begin(), Message_WrongToken, TokenNames[kind], TokenNames[got]); + return false; +} + +bool +Parser::matchName(String **name, TokenKind kind) +{ + assert(kind == TOK_NAME || kind == TOK_LABEL); + *name = NULL; + if (!match(kind)) + return false; + *name = zone_->makeSymbol(scanner_.literal(), scanner_.literal_length()); + return true; +} + +String * +Parser::expectName() +{ + if (!expect(TOK_NAME)) + return NULL; + return zone_->makeSymbol(scanner_.literal(), scanner_.literal_length()); +} + +Expression * +Parser::primitive() +{ + TokenKind kind = scanner_.peek(); + switch (kind) { + case TOK_FLOAT_LITERAL: + { + scanner_.scan(); + double value = Scanner::ParseDouble(scanner_.literal()); + return new (pool_) FloatLiteral(scanner_.begin(), value); + } + + case TOK_HEX_LITERAL: + { + scanner_.scan(); + int value = strtol(scanner_.literal(), NULL, 16); + return new (pool_) IntegerLiteral(scanner_.begin(), value); + } + + case TOK_INTEGER_LITERAL: + { + scanner_.scan(); + int value = atoi(scanner_.literal()); + return new (pool_) IntegerLiteral(scanner_.begin(), value); + } + + case TOK_TRUE: + case TOK_FALSE: + scanner_.scan(); + return new (pool_) BooleanLiteral(scanner_.begin(), kind); + + case TOK_STRING_LITERAL: + { + scanner_.scan(); + Local string(zone_, zone_->makeSymbol(scanner_.literal(), scanner_.literal_length())); + return new (pool_) StringLiteral(scanner_.begin(), string); + } + + default: + { + if (kind != TOK_ERROR) + cc_.reportError(scanner_.begin(), Message_ExpectedExpression, TokenNames[kind]); + return NULL; + } + } +} + +Expression * +Parser::fixedArrayLiteral() +{ + SourcePosition pos = scanner_.begin(); + + ExpressionList *list = new (pool_) ExpressionList(); + do { + // { } is an illegal array literal. + if (list->length() > 0 && match(TOK_LBRACE)) + break; + + Expression *expr = NULL; + if (peek(TOK_LBRACE)) + expr = fixedArrayLiteral(); + else + expr = primitive(); + if (!expr) + return NULL; + + if (!list->append(expr)) + return NULL; + + if (!match(TOK_COMMA)) { + if (!expect(TOK_RBRACE)) + return NULL; + break; + } + } while (true); + + return new (pool_) ArrayLiteral(pos, TOK_LBRACE, list); +} + +Expression * +Parser::prefix() +{ + switch (scanner_.peek()) { + case TOK_LPAREN: + { + scanner_.scan(); + Expression *expr = expression(); + if (!expr) + return NULL; + if (!expect(TOK_RPAREN)) + return NULL; + return expr; + } + + case TOK_NAME: + { + Local name(zone_, expectName()); + if (!name) + return NULL; + return new (pool_) NameProxy(scanner_.begin(), name); + } + + default: + return primitive(); + } +} + +// Parses only old-style declarations. +bool +Parser::typeAndName(Expression **type, String **namep) +{ + if (match(TOK_LABEL)) { + // Old-style declaration: + // new X:type + Local name(zone_, zone_->makeSymbol(scanner_.literal(), scanner_.literal_length())); + if (!name) + return false; + + *type = new (pool_) NameProxy(scanner_.begin(), name); + *namep = expectName(); + return !!*namep; + } + + *type = NULL; + *namep = expectName(); + + return !!*namep; +} + +// Parses both old and new style declarations. +bool +Parser::fullTypeAndName(Expression **type, String **namep, DeclKind kind) +{ + // decl ::= newdecl | olddecl + // newdecl ::= Type Ident + // olddecl ::= Label? (Ident | "...") + // type ::= Ident + // label ::= Ident ":" + // + if (match(TOK_LABEL)) { + // We something of the form: + // Label Ident + // or: Label "..." + Local name(zone_, zone_->makeSymbol(scanner_.literal(), scanner_.literal_length())); + if (!name) + return false; + + *type = new (pool_) NameProxy(scanner_.begin(), name); + if (kind == DeclArg && match(TOK_ELLIPSES)) { + *namep = NULL; + } else { + if ((*namep = expectName()) == NULL) + return false; + } + return true; + } + + Local name(zone_, expectName()); + if (!name) + return false; + + if (peek(TOK_NAME) || (kind == DeclArg && peek(TOK_ELLIPSES))) { + // We got something of the form: + // Ident Ident + *type = new (pool_) NameProxy(scanner_.begin(), name); + if (peek(TOK_NAME)) { + if ((*namep = expectName()) == NULL) + return false; + } else { + *namep = NULL; + } + return true; + } + + // We got something of the form: + // Ident + *type = NULL; + *namep = name; + return true; +} + +Expression * +Parser::call(Expression *callee) +{ + ExpressionList *arguments = new (pool_) ExpressionList(); + + SourcePosition pos = scanner_.begin(); + expect(TOK_LPAREN); + + if (!match(TOK_RPAREN)) { + while (true) { + Expression *expr = expression(); + if (!expr) + return NULL; + + if (!arguments->append(expr)) + return NULL; + + if (!match(TOK_COMMA)) + break; + } + + if (!expect(TOK_RPAREN)) + return NULL; + } + + return new (pool_) CallExpression(pos, callee, arguments); +} + +Expression * +Parser::index(Expression *left) +{ + expect(TOK_LBRACKET); + + SourcePosition pos = scanner_.begin(); + Expression *expr = expression(); + if (!expr) + return NULL; + + if (!expect(TOK_RBRACKET)) + return NULL; + + return new (pool_) IndexExpression(pos, left, expr); +} + +Expression * +Parser::field(Expression *left) +{ + expect(TOK_DOT); + + SourcePosition pos = scanner_.begin(); + Local name(zone_, expectName()); + if (!name) + return NULL; + + return new (pool_) FieldExpression(pos, left, name); +} + +Expression * +Parser::primary() +{ + Expression *expr = prefix(); + if (!expr) + return NULL; + + for (;;) { + switch (scanner_.peek()) { + case TOK_LPAREN: + if ((expr = call(expr)) == NULL) + return NULL; + break; + + case TOK_LBRACKET: + if ((expr = index(expr)) == NULL) + return NULL; + break; + + case TOK_DOT: + if ((expr = field(expr)) == NULL) + return NULL; + break; + + default: + return expr; + } + } +} + +Expression * +Parser::unary() +{ + TokenKind token = scanner_.peek(); + + SourcePosition pos = scanner_.begin(); + switch (token) { + case TOK_INCREMENT: + case TOK_DECREMENT: + { + scanner_.scan(); + Expression *expr = unary(); + if (!expr) + return NULL; + return new (pool_) IncDecExpression(pos, token, expr, false); + } + + case TOK_MINUS: + case TOK_NOT: + case TOK_TILDE: + { + scanner_.scan(); + Expression *expr = unary(); + if (!expr) + return NULL; + return new (pool_) UnaryExpression(pos, token, expr); + } + + case TOK_SIZEOF: + { + scanner_.scan(); + if (!expect(TOK_LPAREN)) + return NULL; + Expression *expr = unary(); + if (!expr) + return NULL; + if (!expect(TOK_RPAREN)) + return NULL; + return new (pool_) UnaryExpression(pos, token, expr); + } + + case TOK_LABEL: + { + scanner_.scan(); + Local tag(zone_, zone_->makeSymbol(scanner_.literal(), scanner_.literal_length())); + if (!tag) + return NULL; + + NameProxy *proxy = new (pool_) NameProxy(scanner_.begin(), tag); + + Expression *expr = unary(); + if (!expr) + return NULL; + + return new (pool_) UnaryExpression(pos, TOK_LABEL, expr, proxy); + } + + default: + break; + } + + Expression *expr = primary(); + + token = scanner_.peek(); + if (token == TOK_INCREMENT || token == TOK_DECREMENT) { + scanner_.scan(); + return new (pool_) IncDecExpression(pos, token, expr, true); + } + + return expr; +} + +Expression * +Parser::multiplication() +{ + Expression *left = unary(); + do { + if (!match(TOK_SLASH) && !match(TOK_STAR) && !match(TOK_PERCENT)) + break; + SourcePosition pos = scanner_.begin(); + TokenKind kind = scanner_.last(); + Expression *right = unary(); + if (!right) + return NULL; + left = new (pool_) BinaryExpression(pos, kind, left, right); + } while (left); + return left; +} + +Expression * +Parser::addition() +{ + Expression *left = multiplication(); + while (left) { + if (!match(TOK_PLUS) && !match(TOK_MINUS)) + break; + SourcePosition pos = scanner_.begin(); + TokenKind kind = scanner_.last(); + Expression *right = multiplication(); + if (!right) + return NULL; + left = new (pool_) BinaryExpression(pos, kind, left, right); + } + return left; +} + +Expression * +Parser::shift() +{ + Expression *left = addition(); + while (left) { + if (!match(TOK_SHL) && !match(TOK_SHR) && !match(TOK_USHR)) + break; + SourcePosition pos = scanner_.begin(); + TokenKind kind = scanner_.last(); + Expression *right = addition(); + if (!right) + return NULL; + left = new (pool_) BinaryExpression(pos, kind, left, right); + } + return left; +} + +Expression * +Parser::bitand_() +{ + Expression *left = shift(); + while (left) { + if (!match(TOK_BITAND)) + break; + SourcePosition pos = scanner_.begin(); + Expression *right = shift(); + if (!right) + return NULL; + left = new (pool_) BinaryExpression(pos, TOK_BITAND, left, right); + } + return left; +} + +Expression * +Parser::bitxor() +{ + Expression *left = bitand_(); + while (left) { + if (!match(TOK_BITXOR)) + break; + SourcePosition pos = scanner_.begin(); + Expression *right = shift(); + if (!right) + return NULL; + left = new (pool_) BinaryExpression(pos, TOK_BITXOR, left, right); + } + return left; +} + +Expression * +Parser::bitor_() +{ + Expression *left = bitxor(); + while (left) { + if (!match(TOK_BITOR)) + break; + SourcePosition pos = scanner_.begin(); + Expression *right = bitxor(); + if (!right) + return NULL; + left = new (pool_) BinaryExpression(pos, TOK_BITOR, left, right); + } + return left; +} + +Expression * +Parser::relational() +{ + Expression *left = bitor_(); + while (left) { + TokenKind kind = scanner_.peek(); + if (kind < TOK_LT || kind > TOK_GE) + break; + scanner_.scan(); + SourcePosition pos = scanner_.begin(); + Expression *right = shift(); + if (!right) + return NULL; + left = new (pool_) BinaryExpression(pos, kind, left, right); + } + return left; +} + +Expression * +Parser::equals() +{ + Expression *left = relational(); + while (left) { + if (!match(TOK_EQUALS) && !match(TOK_NOTEQUALS)) + break; + TokenKind kind = scanner_.last(); + SourcePosition pos = scanner_.begin(); + Expression *right = relational(); + if (!right) + return NULL; + left = new (pool_) BinaryExpression(pos, kind, left, right); + } + return left; +} + +Expression * +Parser::and_() +{ + Expression *left = equals(); + while (left) { + if (!match(TOK_AND)) + break; + SourcePosition pos = scanner_.begin(); + Expression *right = equals(); + if (!right) + return NULL; + left = new (pool_) BinaryExpression(pos, TOK_AND, left, right); + } + return left; +} + +Expression * +Parser::or_() +{ + Expression *left = and_(); + while (left) { + if (!match(TOK_OR)) + break; + SourcePosition pos = scanner_.begin(); + Expression *right = and_(); + if (!right) + return NULL; + left = new (pool_) BinaryExpression(pos, TOK_OR, left, right); + } + return left; +} + +Expression * +Parser::ternary() +{ + Expression *cond = or_(); + if (!cond) + return NULL; + + if (!match(TOK_QMARK)) + return cond; + + Expression *left; + SourcePosition pos = scanner_.begin(); + AutoAllowTags disableTags(scanner_); + { + if ((left = expression()) == NULL) + return NULL; + } + + if (!expect(TOK_COLON)) + return NULL; + + Expression *right = expression(); + if (!right) + return NULL; + + return new (pool_) TernaryExpression(pos, cond, left, right); +} + +Expression * +Parser::assignment() +{ + Expression *left = ternary(); + while (left) { + TokenKind token = scanner_.peek(); + if (token != TOK_ASSIGN && (token < TOK_ASSIGN_ADD || token > TOK_ASSIGN_SHL)) + break; + scanner_.scan(); + SourcePosition pos = scanner_.begin(); + Expression *expr = ternary(); + if (!expr) + return NULL; + + left = new (pool_) Assignment(pos, token, left, expr); + } + return left; +} + +Expression * +Parser::expression() +{ + return assignment(); +} + +Statement * +Parser::while_() +{ + // while ::= "while" "(" expr ")" statement + SourcePosition pos = scanner_.begin(); + scanner_.scan(); + + if (!expect(TOK_LPAREN)) + return NULL; + + Expression *condition = expression(); + if (!condition) + return NULL; + + if (!expect(TOK_RPAREN)) + return NULL; + + Statement *body; + if (scanner_.peek() == TOK_LBRACE) + body = block(); + else + body = localStatement(); + if (!body) + return NULL; + + return new (pool_) WhileStatement(pos, TOK_WHILE, condition, body); +} + +Statement * +Parser::do_() +{ + // do ::= "do" block "while" "(" expr ")" + SourcePosition pos = scanner_.begin(); + scanner_.scan(); + + Statement *body = block(); + if (!body) + return NULL; + + if (!expect(TOK_WHILE)) + return NULL; + + if (!expect(TOK_LPAREN)) + return NULL; + Expression *condition = expression(); + if (!condition) + return NULL; + if (!expect(TOK_RPAREN)) + return NULL; + + return new (pool_) WhileStatement(pos, TOK_DO, condition, body); +} + +Statement * +Parser::switch_() +{ + // switch ::= "switch" "(" expr ")" "{" case* defaultcase? "}" + // case ::= "case" casevals ":" statement + // defaultcase ::= "default" ":" statement + // + expect(TOK_SWITCH); + + SourcePosition pos = scanner_.begin(); + + if (!expect(TOK_LPAREN)) + return NULL; + Expression *expr = expression(); + if (!expr) + return NULL; + if (!expect(TOK_RPAREN)) + return NULL; + + if (!expect(TOK_LBRACE)) + return NULL; + + SourcePosition defaultPos; + PoolList *cases = new (pool_) PoolList(); + + Statement *defaultCase = NULL; + + while (!peek(TOK_RBRACE)) { + Expression *expr = NULL; + ExpressionList *others = NULL; + if (match(TOK_DEFAULT)) { + if (defaultCase) { + cc_.reportError(scanner_.begin(), Message_OneDefaultPerSwitch); + return NULL; + } + + defaultPos = scanner_.begin(); + } else { + if (defaultCase) { + cc_.reportError(defaultPos, Message_DefaultMustBeLastCase); + return NULL; + } + + if (!expect(TOK_CASE)) + return NULL; + + // A limitation in the grammar is that |case :| will be + // detected as a label. + AutoAllowTags disableTags(scanner_); + + if ((expr = expression()) == NULL) + return NULL; + + if (peek(TOK_COMMA)) { + others = new (pool_) ExpressionList(); + while (match(TOK_COMMA)) { + Expression *other = expression(); + if (!other) + return NULL; + if (!others->append(other)) + return NULL; + } + } + } + + if (!expect(TOK_COLON)) + return NULL; + + Statement *statement = localStatement(); + if (!statement) + return NULL; + + if (!peek(TOK_CASE) && + !peek(TOK_DEFAULT) && + !peek(TOK_RBRACE)) + { + cc_.reportError(scanner_.begin(), Message_SingleStatementPerCase); + return NULL; + } + + if (expr) { + Case *caze = new (pool_) Case(expr, others, statement); + if (!cases->append(caze)) + return NULL; + } else { + defaultCase = statement; + } + } + + if (!expect(TOK_RBRACE)) + return NULL; + + return new (pool_) SwitchStatement(pos, expr, cases, defaultCase); +} + +Statement * +Parser::forStatement() +{ + // for ::= "for" "(" forinit? ";" forcond? ";" forstep ")" statement + // forint ::= "new" vardecl | + // exprstmt + // forcond ::= expr + // forstep ::= exprstmt + // + SourcePosition pos = scanner_.begin(); + scanner_.scan(); + if (!expect(TOK_LPAREN)) + return NULL; + + Statement *decl = NULL; + if (!match(TOK_SEMICOLON)) { + if (match(TOK_NEW)) { + if ((decl = vars(TypeQual_None)) == NULL) + return NULL; + } else { + if ((decl = expressionStatement()) == NULL) + return NULL; + } + if (!expect(TOK_SEMICOLON)) + return NULL; + } + + Expression *condition = NULL; + if (!match(TOK_SEMICOLON)) { + if ((condition = expression()) == NULL) + return NULL; + if (!expect(TOK_SEMICOLON)) + return NULL; + } + + Statement *update = NULL; + if (!match(TOK_RPAREN)) { + if ((update = expressionStatement()) == NULL) + return NULL; + if (!expect(TOK_RPAREN)) + return NULL; + } + + Statement *body; + if (scanner_.peek() == TOK_LBRACE) + body = block(); + else + body = localStatement(); + if (!body) + return NULL; + + return new (pool_) ForStatement(pos, decl, condition, update, body); +} + +ExpressionList * +Parser::dimensions() +{ + // dimensions ::= ("[" expr? "]")* + + ExpressionList *postDimensions = new (pool_) ExpressionList(); + while (match(TOK_LBRACKET)) { + Expression *dim = NULL; + if (!match(TOK_RBRACKET)) { + if ((dim = expression()) == NULL) + return NULL; + if (!expect(TOK_RBRACKET)) + return NULL; + } + + if (!postDimensions->append(dim)) + return NULL; + } + return postDimensions; +} + +VariableDeclaration * +Parser::var(TypeQualifiers qual) +{ + // vardecl ::= decl ("=" expr)? + + Expression *type; + Local name(zone_); + + if (!typeAndName(&type, name.ref())) + return NULL; + + ExpressionList *dims = NULL; + if (peek(TOK_LBRACKET)) { + if ((dims = dimensions()) == NULL) + return NULL;; + } + + SourcePosition pos = scanner_.begin(); + + Expression *expr = NULL; + if (match(TOK_ASSIGN)) { + switch (scanner_.peek()) { + // Note: we don't support [] literals yet. + case TOK_LBRACE: + scanner_.scan(); + if ((expr = fixedArrayLiteral()) == NULL) + return NULL; + break; + + default: + if ((expr = expression()) == NULL) + return NULL; + break; + } + } + + return new (pool_) VariableDeclaration(pos, qual, name, type, dims, expr); +} + +Statement * +Parser::vars(TypeQualifiers qual) +{ + // localvars ::= ("new" | "const" | "decl") vardecl ("," vardecl)* + // globalvars ::= ("public" | "const" | "new" | "public" "const") vardecl + VariableDeclaration *decl = var(qual); + if (!decl) + return NULL; + + if (!peek(TOK_COMMA)) + return decl; + + StatementList *statements = new (pool_) StatementList(); + if (!statements->append(decl)) + return NULL; + + while (match(TOK_COMMA)) { + Statement *decl = var(qual); + if (!decl) + return NULL; + if (!statements->append(decl)) + return NULL; + } + + // Create a fake-o block with no scope to create a statement list. + return new (pool_) BlockStatement(decl->pos(), statements, TOK_NEW); +} + +Statement * +Parser::returnStatement() +{ + // return ::= "return" term | + // "return" "expr" + + SourcePosition pos = scanner_.begin(); + if (!expect(TOK_RETURN)) + return NULL; + + Expression *expr = NULL; + if (scanner_.peek() != TOK_SEMICOLON) { + if ((expr = expression()) == NULL) + return NULL; + + // We only care about non-void returns when determining whether a + // tagless function is non-void. + encounteredReturn_ = true; + } + + return new (pool_) ReturnStatement(pos, expr); +} + +Statement * +Parser::expressionStatement() +{ + // exprstmt ::= expr + Expression *left = assignment(); + if (!left) + return NULL; + + return new (pool_) ExpressionStatement(left); +} + +Statement * +Parser::block() +{ + // block ::= "{" statement* "}" + + if (!expect(TOK_LBRACE)) + return NULL; + + SourcePosition pos = scanner_.begin(); + + StatementList *statements = new (pool_) StatementList(); + while (!match(TOK_RBRACE)) { + Statement *statement = localStatement(); + if (statement == NULL) + return NULL; + if (!statements->append(statement)) + return NULL; + } + + BlockStatement *block = new (pool_) BlockStatement(pos, statements, TOK_LBRACE); + return block; +} + +Statement * +Parser::if_() +{ + // if ::= "if" "(" expr ")" statement elseif* else? + // elseif ::= "elseif" "(" expr ")" statement + // else ::= "else" statement + expect(TOK_IF); + + SourcePosition pos = scanner_.begin(); + if (!expect(TOK_LPAREN)) + return NULL; + + Expression *cond = expression(); + if (!cond) + return NULL; + + if (!expect(TOK_RPAREN)) + return NULL; + + Statement *ifTrue = localStatement(); + if (!ifTrue) + return NULL; + + IfStatement *outer = new (pool_) IfStatement(pos, cond, ifTrue); + + IfStatement *last = outer; + while (match(TOK_ELSE)) { + if (!match(TOK_IF)) { + Statement *ifFalse = localStatement(); + if (!ifFalse) + return NULL; + + last->setIfFalse(ifFalse); + break; + } + + SourcePosition pos = scanner_.begin(); + if (!expect(TOK_LPAREN)) + return NULL; + + Expression *otherCond = expression(); + if (!otherCond) + return NULL; + + if (!expect(TOK_RPAREN)) + return NULL; + + Statement *otherIfTrue = localStatement(); + if (!otherIfTrue) + return NULL; + + IfStatement *inner = new (pool_) IfStatement(pos, otherCond, otherIfTrue); + last->setIfFalse(inner); + last = inner; + } + + return outer; +} + +Statement * +Parser::localStatement() +{ + // statement ::= stmt term + // stmt ::= do | for | if | while | struct | enum | + // localvars | return | switch | break | continue + + Statement *statement = NULL; + + // Statements which must be followed by a semicolon will break out of the + // switch. If they may end in BlockStatements, they'll immediately return. + + TokenKind kind = scanner_.peek(); + switch (kind) { + case TOK_CONST: + case TOK_NEW: + { + TypeQualifiers quals = (kind == TOK_CONST) + ? TypeQual_Const + : TypeQual_None; + scanner_.scan(); + if ((statement = vars(quals)) == NULL) + return NULL; + break; + } + + case TOK_FOR: + return forStatement(); + + case TOK_WHILE: + return while_(); + + case TOK_BREAK: + scanner_.scan(); + statement = new (pool_) BreakStatement(scanner_.begin()); + break; + + case TOK_CONTINUE: + scanner_.scan(); + statement = new (pool_) ContinueStatement(scanner_.begin()); + break; + + case TOK_DO: + if ((statement = do_()) == NULL) + return NULL; + break; + + case TOK_RETURN: + if ((statement = returnStatement()) == NULL) + return NULL; + break; + + case TOK_ENUM: + if ((statement = enum_(TOK_NONE)) == NULL) + return NULL; + break; + + case TOK_STRUCT: + if ((statement = struct_()) == NULL) + return NULL; + break; + + case TOK_SWITCH: + return switch_(); + + case TOK_IF: + return if_(); + + case TOK_LBRACE: + return block(); + + default: + break; + } + + if (!statement) { + if ((statement = expressionStatement()) == NULL) + return NULL; + } + + if (!expect(TOK_SEMICOLON)) + return NULL; + + return statement; +} + +Statement * +Parser::struct_() +{ + // struct ::= "struct" name { struct_fields } + // struct_fields ::= struct_field | + // struct_field struct_fields + // struct_field ::= label? name term + expect(TOK_STRUCT); + + Local name(zone_, expectName()); + if (!name) + return NULL; + + SourcePosition pos = scanner_.begin(); + + if (!expect(TOK_LBRACE)) + return NULL; + + FieldList *fields = new (pool_) FieldList(); + + while (!peek(TOK_RBRACE)) { + Expression *type; + Local fieldName(zone_); + + if (!fullTypeAndName(&type, fieldName.ref(), DeclField)) + return NULL; + + SourcePosition pos = scanner_.begin(); + + if (!matchSemicolon()) + return NULL; + + StructureStatement::Field *field = + new (pool_) StructureStatement::Field(pos, type, fieldName); + if (!field || !fields->append(field)) + return NULL; + } + + if (!expect(TOK_RBRACE)) + return NULL; + + if (fields->empty()) { + cc_.reportError(pos, Message_StructCantBeEmpty); + return NULL; + } + + return new (pool_) StructureStatement(pos, name, fields); +} + +Statement * +Parser::enum_(TokenKind kind) +{ + // enum ::= "enum" name? { enum_members? } + // enum_members ::= enum_member ","? | + // enum_member "," enum_members + // enum_member ::= ident ("=" constexpr)? + expect(TOK_ENUM); + + SourcePosition pos = scanner_.begin(); + + Local name(zone_); + if (matchName(name.ref()) && !name) + return NULL; + + EnumStatement::EntryList *entries = new (pool_) EnumStatement::EntryList(); + + if (!expect(TOK_LBRACE)) + return NULL; + + do { + if (scanner_.peek() == TOK_RBRACE) + break; + + SourcePosition pos = scanner_.begin(); + Local name(zone_, expectName()); + if (!name) + return NULL; + + Expression *expr = NULL; + if (match(TOK_ASSIGN)) { + if ((expr = expression()) == NULL) + return NULL; + } + + if (!entries->append(EnumStatement::Entry(name, expr, pos))) + return NULL; + } while (match(TOK_COMMA)); + if (!expect(TOK_RBRACE)) + return NULL; + + return new (pool_) EnumStatement(pos, kind, name, entries); +} + +template +static inline ScopedRoot * +NewScopedRoot(PoolAllocator &pool, Handle obj) +{ + return new (pool) ScopedRoot(obj); +} + +ImportStatement * +Parser::import(TokenKind kind) +{ + SourcePosition pos = scanner_.begin(); + + // import ::= "from" import_path "import" import_names + // | "import" import_path + // | "import" import_path ".*" + // import_path ::= name + // | name "." import_path + // import_names ::= "*" + // | import_name_list + // import_name_list ::= name + // | name "," import_name_list + + Local name(zone_, expectName()); + if (!name) + return NULL; + + PathComponent *root = new (pool_) PathComponent(NewScopedRoot(pool_, name), NULL); + PathComponent *current = root; + + // NULL namelist means that we do not import any names. An empty namelist + // means we import all names. + NameList *names = NULL; + + while (match(TOK_DOT)) { + if (kind == TOK_IMPORT && match(TOK_STAR)) { + names = new (pool_) NameList(); + break; + } + + if ((name = expectName()) == NULL) + return NULL; + + current->next = new (pool_) PathComponent(NewScopedRoot(pool_, name), NULL); + current = current->next; + } + + if (kind == TOK_FROM) { + if (!expect(TOK_IMPORT)) + return NULL; + + names = new (pool_) NameList(); + + if (!match(TOK_STAR)) { + if (!matchName(name.address())) { + cc_.reportError(scanner_.begin(), Message_BadImportFrom); + return NULL; + } + + if (!name || !names->append(NewScopedRoot(pool_, name))) + return NULL; + + while (match(TOK_COMMA)) { + if ((name = expectName()) == NULL) + return NULL; + if (!names->append(NewScopedRoot(pool_, name))) + return NULL; + } + } + } + + return new (pool_) ImportStatement(pos, root, names); +} + +Statement * +Parser::function(TokenKind token) +{ + Expression *returnType; + Local name(zone_); + + if (!fullTypeAndName(&returnType, name.ref(), DeclFun)) + return NULL; + + SourcePosition pos = scanner_.begin(); + ParameterList *parameters = new (pool_) ParameterList(); + + if (!expect(TOK_LPAREN)) + return NULL; + + bool variadic = false; + + if (!match(TOK_RPAREN)) { + while (true) { + TypeQualifiers quals = TypeQual_None; + if (match(TOK_CONST)) + quals = TypeQualifiers(quals | TypeQual_Const); + + bool ref = match(TOK_AMPERSAND); + + SourcePosition begin = scanner_.begin(); + Expression *type; + Local name(zone_); + if (!fullTypeAndName(&type, name.ref(), DeclArg)) + return NULL; + + if (!name) { + if ((quals & TypeQual_Const) || ref) { + cc_.reportError(begin, Message_InvalidVarArgs); + return NULL; + } + + if (token != TOK_NATIVE) { + cc_.reportError(begin, Message_VarArgsOutsideNative); + return NULL; + } + + variadic = true; + break; + } + + ExpressionList *dims = NULL; + if (peek(TOK_LBRACKET)) { + if ((dims = dimensions()) == NULL) + return NULL; + } + + Expression *expr = NULL; + if (match(TOK_ASSIGN)) { + if ((expr = expression()) == NULL) + return NULL; + } + + Parameter *parameter = new (pool_) Parameter(name, type, ref, quals, dims, expr, begin); + if (!parameters->append(parameter)) + return NULL; + + if (!match(TOK_COMMA)) + break; + } + if (!expect(TOK_RPAREN)) + return NULL; + } + + encounteredReturn_ = false; + + Statement *body = NULL; + if (token != TOK_FORWARD && token != TOK_NATIVE && token != TOK_TYPEDEF) { + body = block(); + if (!body) + return NULL; + } + + FunctionSignature signature(returnType, parameters, variadic); + if (token == TOK_TYPEDEF) + return new (pool_) FunctionTypeStatement(pos, name, signature); + + return new (pool_) FunctionStatement(pos, name, token, body, signature, encounteredReturn_); +} + +static inline bool +DeclareIntrinsicType(Zone *zone, Scope *scope, const char *name, Handle type) +{ + Local tag(zone, zone->makeSymbol(name)); + if (!tag) + return false; + + assert(false); + return false; //return !!scope->declareType(tag, type); +} + +static inline bool +DeclareIntrinsicType(Zone *zone, Scope *scope, const char *name, PrimitiveType type) +{ + Local primitive(zone, zone->types()->getPrimitive(type)); + assert(primitive); + return DeclareIntrinsicType(zone, scope, name, primitive); +} + +ParseTree * +Parser::parse() +{ + StatementList *statements = new (pool_) StatementList(); + + for (;;) { + Statement *statement = NULL; + + TokenKind kind = scanner_.peek(); + switch (kind) { + case TOK_ERROR: + return NULL; + + case TOK_EOF: + break; + + case TOK_NAME: + if ((statement = function(TOK_NAME)) == NULL) + return NULL; + break; + + case TOK_FROM: + case TOK_IMPORT: + { + scanner_.scan(); + if (statements->length()) + cc_.reportError(scanner_.begin(), Message_ImportsMustBeFirst); + ImportStatement *stmt = import(kind); + if (!stmt) + return NULL; + if (!imports_->append(stmt)) + return NULL; + if (!matchSemicolon()) + return NULL; + continue; + } + + case TOK_FORWARD: + case TOK_PUBLIC: + case TOK_NATIVE: + case TOK_STOCK: + case TOK_LABEL: + case TOK_TYPEDEF: + { + if (kind != TOK_LABEL) + scanner_.scan(); + if (kind == TOK_PUBLIC && peek(TOK_CONST)) { + if ((statement = var(TypeQual_Const)) == NULL) + return NULL; + if (!matchSemicolon()) + return NULL; + } else if (kind == TOK_PUBLIC && peek(TOK_ENUM)) { + if ((statement = enum_(kind)) == NULL) + return NULL; + if (!matchSemicolon()) + return NULL; + } else { + if ((statement = function(kind)) == NULL) + return NULL; + if ((kind == TOK_FORWARD || kind == TOK_NATIVE || kind == TOK_TYPEDEF) && !matchSemicolon()) + return NULL; + } + break; + } + + case TOK_CONST: + case TOK_NEW: + { + TypeQualifiers quals = (kind == TOK_CONST) ? TypeQual_Const : TypeQual_None; + // Note: at global scope, we don't accept comma-delimited + // declarations yet, to see if we can get away with forcing + // a nicer top-level style. + scanner_.scan(); + if ((statement = var(quals)) == NULL) + return NULL; + if (!matchSemicolon()) + return NULL; + break; + } + + case TOK_ENUM: + if ((statement = enum_(TOK_NONE)) == NULL) + return NULL; + if (!matchSemicolon()) + return NULL; + break; + + case TOK_STRUCT: + if ((statement = struct_()) == NULL) + return NULL; + if (!matchSemicolon()) + return NULL; + break; + + default: + cc_.reportError(scanner_.begin(), Message_ExpectedGlobal); + return NULL; + } + + if (!statement) + break; + + if (!statements->append(statement)) + return NULL; + } + + return new (pool_) ParseTree(statements, unbound_, imports_); +} + +class AstPrinter : public AstVisitor +{ + FILE *fp_; + size_t level_; + + private: + void prefix() { + for (size_t i = 0; i < level_; i++) + fprintf(fp_, " "); + } + void indent() { + level_++; + } + void unindent() { + level_--; + } + + public: + AstPrinter(FILE *fp) + : fp_(fp), + level_(0) + { + } + + void dump(const FunctionSignature &sig) { + prefix(); + fprintf(fp_, "()\n"); + } + void visitNameProxy(NameProxy *name) { + prefix(); + fprintf(fp_, "[ NameProxy (%s)\n", name->name()->chars()); + } + void visitCallExpression(CallExpression *node) { + prefix(); + fprintf(fp_, "[ CallExpression\n"); + indent(); + node->callee()->accept(this); + for (size_t i = 0; i < node->arguments()->length(); i++) + node->arguments()->at(i)->accept(this); + unindent(); + } + void visitFunctionTypeStatement(FunctionTypeStatement *node) { + prefix(); + fprintf(fp_, "[ FunctionTypeStatement (%s)\n", node->name()->chars()); + indent(); + dump(node->signature()); + unindent(); + } + void visitFunctionStatement(FunctionStatement *node) { + prefix(); + fprintf(fp_, "[ FunctionStatement (%s)\n", node->name()->chars()); + indent(); + dump(node->signature()); + if (node->body()) + node->body()->accept(this); + unindent(); + } + void visitExpressionStatement(ExpressionStatement *node) { + prefix(); + fprintf(fp_, "[ ExpressionStatement\n"); + indent(); + node->expression()->accept(this); + unindent(); + } + void visitAssignment(Assignment *node) { + prefix(); + fprintf(fp_, "[ Assignment\n"); + indent(); + node->lvalue()->accept(this); + node->expression()->accept(this); + unindent(); + } + void visitTernaryExpression(TernaryExpression *node) { + prefix(); + fprintf(fp_, "[ TernaryExpression\n"); + indent(); + node->condition()->accept(this); + node->left()->accept(this); + node->right()->accept(this); + unindent(); + } + void visitBinaryExpression(BinaryExpression *node) { + prefix(); + fprintf(fp_, "[ BinaryExpression (%s)\n", TokenNames[node->token()]); + indent(); + node->left()->accept(this); + node->right()->accept(this); + unindent(); + } + void visitUnaryExpression(UnaryExpression *node) { + prefix(); + fprintf(fp_, "[ UnaryExpression (%s)\n", TokenNames[node->token()]); + indent(); + node->expression()->accept(this); + unindent(); + } + void visitReturnStatement(ReturnStatement *node) { + prefix(); + fprintf(fp_, "[ ReturnStatement\n"); + indent(); + if (node->expression()) + node->expression()->accept(this); + unindent(); + } + void visitForStatement(ForStatement *node) { + prefix(); + fprintf(fp_, "[ ForStatement\n"); + indent(); + if (node->initialization()) + node->initialization()->accept(this); + if (node->condition()) + node->condition()->accept(this); + if (node->update()) + node->update()->accept(this); + node->body()->accept(this); + unindent(); + } + void visitBlockStatement(BlockStatement *node) { + prefix(); + fprintf(fp_, "[ BlockStatement\n"); + indent(); + for (size_t i = 0; i < node->statements()->length(); i++) + node->statements()->at(i)->accept(this); + unindent(); + } + void visitVariableDeclaration(VariableDeclaration *node) { + prefix(); + fprintf(fp_, "[ VariableDeclaration (%s)\n", node->name()->chars()); + indent(); + if (node->initialization()) + node->initialization()->accept(this); + unindent(); + } + void visitIntegerLiteral(IntegerLiteral *node) { + prefix(); + fprintf(fp_, "[ IntegerLiteral (%d)\n", node->value()); + } + void visitBooleanLiteral(BooleanLiteral *node) { + prefix(); + fprintf(fp_, "[ BooleanLiteral (%s)\n", + (node->token() == TOK_TRUE) ? "true" : "false"); + } + void visitFloatLiteral(FloatLiteral *node) { + prefix(); + fprintf(fp_, "[ FloatLiteral (%f)\n", node->value()); + } + void visitIfStatement(IfStatement *node) { + prefix(); + fprintf(fp_, "[ IfStatement\n"); + indent(); + node->ifTrue()->accept(this); + if (node->ifFalse()) + node->ifFalse()->accept(this); + unindent(); + } + void visitIndexExpression(IndexExpression *node) { + prefix(); + fprintf(fp_, "[ IndexExpression\n"); + indent(); + node->left()->accept(this); + node->right()->accept(this); + unindent(); + } + void visitEnumStatement(EnumStatement *node) { + prefix(); + fprintf(fp_, "[ EnumStatement (%s)\n", node->name() ? node->name()->chars() : ""); + indent(); + for (size_t i = 0; i < node->entries()->length(); i++) { + prefix(); + fprintf(fp_, "%s =\n", node->entries()->at(i).name->chars()); + if (node->entries()->at(i).expr) { + indent(); + node->entries()->at(i).expr->accept(this); + unindent(); + } + } + unindent(); + } + void visitWhileStatement(WhileStatement *node) { + prefix(); + fprintf(fp_, "[ WhileStatement (%s)\n", + (node->token() == TOK_DO) ? "do" : "while"); + indent(); + if (node->token() == TOK_DO) { + node->condition()->accept(this); + node->body()->accept(this); + } else { + node->condition()->accept(this); + node->body()->accept(this); + } + unindent(); + } + void visitBreakStatement(BreakStatement *node) { + prefix(); + fprintf(fp_, "[ BreakStatement\n"); + } + void visitContinueStatement(ContinueStatement *node) { + prefix(); + fprintf(fp_, "[ ContinueStatement\n"); + } + void visitStringLiteral(StringLiteral *node) { + prefix(); + fprintf(fp_, "[ StringLiteral\n"); + } + void visitIncDecExpression(IncDecExpression *node) { + prefix(); + fprintf(fp_, "[ IncDecExpression (postfix=%d)\n", node->postfix()); + indent(); + node->expression()->accept(this); + unindent(); + } + void visitStructureStatement(StructureStatement *node) { + prefix(); + fprintf(fp_, "[ StructureStatement %s\n", node->name()->chars()); + } + void visitFieldExpression(FieldExpression *node) { + prefix(); + fprintf(fp_, "[ FieldExpression %s\n", node->field()->chars()); + indent(); + node->left()->accept(this); + unindent(); + } + void visitSwitchStatement(SwitchStatement *node) { + prefix(); + fprintf(fp_, "[ SwitchStatement\n"); + indent(); + node->expression()->accept(this); + for (size_t i = 0; i < node->cases()->length(); i++) { + Case *c = node->cases()->at(i); + c->expression()->accept(this); + if (c->others()) { + for (size_t j = 0; j < c->others()->length(); j++) + c->others()->at(j)->accept(this); + } + indent(); + c->statement()->accept(this); + unindent(); + } + if (node->defaultCase()) + node->defaultCase()->accept(this); + unindent(); + } + void visitArrayLiteral(ArrayLiteral *node) { + prefix(); + fprintf(fp_, "[ ArrayLiteral\n"); + indent(); + for (size_t i = 0; i < node->expressions()->length(); i++) { + Expression *expr = node->expressions()->at(i); + indent(); + expr->accept(this); + unindent(); + } + } + void visitImportStatement(ImportStatement *node) { + prefix(); + fprintf(fp_, "[ ImportStatement\n"); + } +}; + +void +ParseTree::dump(FILE *fp) +{ + AstPrinter printer(fp); + + for (size_t i = 0; i < statements_->length(); i++) + statements_->at(i)->accept(&printer); +} diff -r c4b1341297e5 -r ba85a47ee414 src/compiler/Parser.h --- a/src/compiler/Parser.h Mon Jun 10 00:46:29 2013 +0100 +++ b/src/compiler/Parser.h Mon Jun 10 01:04:43 2013 +0100 @@ -1,106 +1,106 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#ifndef _include_jitcraft_parser_h_ -#define _include_jitcraft_parser_h_ - -#include -#include "../Strings.h" -#include "AST.h" -#include "Scanner.h" -#include "Scopes.h" - -namespace ke { - -class Parser -{ - Zone *zone_; - CompileContext &cc_; - PoolAllocator &pool_; - Scanner scanner_; - PoolList *unbound_; - PoolList *imports_; - bool encounteredReturn_; - - private: - bool peek(TokenKind kind); - bool match(TokenKind kind); - bool expect(TokenKind kind); - bool matchSemicolon(); - - enum DeclKind { - DeclArg, - DeclFun, - DeclField - }; - - VariableDeclaration *var(TypeQualifiers qual); - Statement *vars(TypeQualifiers qual); - bool matchName(String **name, TokenKind kind = TOK_NAME); - bool typeAndName(Expression **type, String **namep); - bool fullTypeAndName(Expression **type, String **namep, DeclKind kind); - - ExpressionList *dimensions(); - String *expectName(); - - Expression *primitive(); - Expression *fixedArrayLiteral(); - Expression *call(Expression *callee); - Expression *field(Expression *left); - Expression *index(Expression *left); - Expression *prefix(); - Expression *primary(); - Expression *unary(); - Expression *multiplication(); - Expression *addition(); - Expression *shift(); - Expression *bitand_(); - Expression *bitxor(); - Expression *bitor_(); - Expression *relational(); - Expression *equals(); - Expression *and_(); - Expression *or_(); - Expression *ternary(); - Expression *assignment(); - Expression *expression(); - - Statement *switch_(); - Statement *struct_(); - Statement *enum_(TokenKind kind); - Statement *if_(); - Statement *block(); - Statement *function(TokenKind tok); - Statement *expressionStatement(); - Statement *while_(); - Statement *do_(); - Statement *forStatement(); - Statement *localStatement(); - Statement *globalStatement(); - Statement *returnStatement(); - ImportStatement *import(TokenKind kind); - - public: - Parser(Zone *zone, CompileContext &cc, const char *stream, size_t length); - - ParseTree *parse(); -}; - -} - -#endif // _include_jitcraft_parser_h_ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_parser_h_ +#define _include_jitcraft_parser_h_ + +#include +#include "../Strings.h" +#include "AST.h" +#include "Scanner.h" +#include "Scopes.h" + +namespace ke { + +class Parser +{ + Zone *zone_; + CompileContext &cc_; + PoolAllocator &pool_; + Scanner scanner_; + PoolList *unbound_; + PoolList *imports_; + bool encounteredReturn_; + + private: + bool peek(TokenKind kind); + bool match(TokenKind kind); + bool expect(TokenKind kind); + bool matchSemicolon(); + + enum DeclKind { + DeclArg, + DeclFun, + DeclField + }; + + VariableDeclaration *var(TypeQualifiers qual); + Statement *vars(TypeQualifiers qual); + bool matchName(String **name, TokenKind kind = TOK_NAME); + bool typeAndName(Expression **type, String **namep); + bool fullTypeAndName(Expression **type, String **namep, DeclKind kind); + + ExpressionList *dimensions(); + String *expectName(); + + Expression *primitive(); + Expression *fixedArrayLiteral(); + Expression *call(Expression *callee); + Expression *field(Expression *left); + Expression *index(Expression *left); + Expression *prefix(); + Expression *primary(); + Expression *unary(); + Expression *multiplication(); + Expression *addition(); + Expression *shift(); + Expression *bitand_(); + Expression *bitxor(); + Expression *bitor_(); + Expression *relational(); + Expression *equals(); + Expression *and_(); + Expression *or_(); + Expression *ternary(); + Expression *assignment(); + Expression *expression(); + + Statement *switch_(); + Statement *struct_(); + Statement *enum_(TokenKind kind); + Statement *if_(); + Statement *block(); + Statement *function(TokenKind tok); + Statement *expressionStatement(); + Statement *while_(); + Statement *do_(); + Statement *forStatement(); + Statement *localStatement(); + Statement *globalStatement(); + Statement *returnStatement(); + ImportStatement *import(TokenKind kind); + + public: + Parser(Zone *zone, CompileContext &cc, const char *stream, size_t length); + + ParseTree *parse(); +}; + +} + +#endif // _include_jitcraft_parser_h_ diff -r c4b1341297e5 -r ba85a47ee414 src/compiler/Scanner.cpp --- a/src/compiler/Scanner.cpp Mon Jun 10 00:46:29 2013 +0100 +++ b/src/compiler/Scanner.cpp Mon Jun 10 01:04:43 2013 +0100 @@ -1,685 +1,685 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of JITCraft. - * - * JITCraft is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * JITCraft. If not, see http://www.gnu.org/licenses/. - */ -#include -#include -#include -#include -#include -#include "../Utility.h" -#include "../Heap-inl.h" -#include "../Handles-inl.h" -#include "Scanner.h" - -using namespace ke; - -const char *ke::TokenNames[] = -{ -#define _(name, str) str, - TOKENMAP(_) -#undef _ - NULL -}; - -Scanner::Scanner(CompileContext &cc, const char *stream, size_t length) - : cc_(cc), - stream_(stream), - length_(length), - pushed_(false), - allowTags_(true), - saved_('\0'), - pos_(stream_) -{ - cur_.line = 1; - cur_.col = 1; - last_ = cur_; -} - -char -Scanner::getChar() -{ - if (saved_ != '\0') { - char temp = saved_; - saved_ = '\0'; - return temp; - } - if (pos_ >= stream_ + length_) - return '\0'; - last_.col = cur_.col++; - return *pos_++; -} - -void -Scanner::putChar(char c) -{ - assert(saved_ == '\0'); - saved_ = c; - cur_.col = last_.col; -} - -void -Scanner::calcNewLine(char c) -{ - cur_.line++; - cur_.col = 1; - last_ = cur_; - - char o; - if (c == '\r' && ((o = getChar()) != '\n')) - putChar(o); -} - -void -Scanner::eatSingleLine() -{ - char c; - while ((c = getChar()) != '\0') { - if (c == '\n' || c == '\r') { - calcNewLine(c); - break; - } - } -} - -bool -Scanner::eatComment() -{ - char c; - - while ((c = getChar()) != '\0') { - while (c == '*') { - c = getChar(); - if (c == '\0') - return false; - if (c == '/') - return true; - } - if (c == '\r' || c == '\n') - calcNewLine(c); - } - - return false; -} - -static inline bool -IsDigit(char c) -{ - return c >= '0' && c <= '9'; -} - -// Based off the logic in sc2.c's ftoi()... -double -Scanner::ParseDouble(const char *string) -{ - const char *ptr = string; - - double number = 0.0; - while (IsDigit(*ptr)) { - number = (number * 10) + (*ptr - '0'); - ptr++; - } - - assert(*ptr == '.'); - ptr++; - - double fraction = 0.0; - double multiplier = 1.0; - while (IsDigit(*ptr)) { - fraction = (fraction * 10) + (*ptr - '0'); - multiplier = multiplier / 10.0; - ptr++; - } - - number += fraction * multiplier; - - if (*ptr++ == 'e') { - int sign = 1; - if (*ptr == '-') { - sign = -1; - ptr++; - } - - int exponent = 0; - while (IsDigit(*ptr)) { - exponent = (exponent * 10) + (*ptr - '0'); - ptr++; - } - - multiplier = pow(10.0, exponent * sign); - number *= multiplier; - } - - return number; -} - -static inline bool -IsHexDigit(char c) -{ - return IsDigit(c) || - (c >= 'a' && c <= 'f') || - (c >= 'A' && c <= 'F'); -} - -// Based on the logic for litchar() in sc2.c. -int -Scanner::readEscapeCode() -{ - char c = getChar(); - if (c == '\\') - return c; - - switch (c) { - case 'a': - return '\a'; - case 'b': - return '\b'; - case 'e': - return 27; // Apparently \e is non-standard. - case 'f': - return '\f'; - case 'n': - return '\n'; - case 'r': - return '\r'; - case 't': - return '\t'; - case 'v': - return '\v'; - - case 'x': - { - unsigned digits = 0; - char r = 0; - - c = getChar(); - while (IsHexDigit(c) && digits < 2) { - if (IsDigit(c)) - c = (c << 4) + (c - '0'); - else - c = (c << 4) + (tolower(c) - 'a' + 10); - digits++; - c = getChar(); - } - - // Swallow a trailing ';' - if (c != ';') - putChar(c); - - return r; - } - - case '\'': - case '\"': - case '%': - return c; - - default: - { - if (IsDigit(c)) { - // \ddd - char r = 0; - while (IsDigit(c)) { - r = r * 10 + (c - '0'); - c = getChar(); - } - - // Swallow a trailing ; - if (c != ';') - putChar(c); - - return r; - } - cc_.reportError(begin(), Message_UnknownEscapeCode, c); - return INT_MAX; - } - } -} - -TokenKind -Scanner::stringLiteral() -{ - literal_.clear(); - - for (;;) { - char c = getChar(); - if (c == '\"') - break; - if (c == '\r' || c == '\n' || c == '\0') { - cc_.reportError(begin(), Message_UnterminatedString); - return TOK_ERROR; - } - if (c == '\\') { - int code = readEscapeCode(); - if (code == INT_MAX) - return TOK_ERROR; - c = char(code); - } - if (!literal_.append(c)) - return TOK_ERROR; - } - - if (!literal_.append('\0')) - return TOK_ERROR; - - return TOK_STRING_LITERAL; -} - -TokenKind -Scanner::hexLiteral() -{ - literal_.clear(); - - char c; - for (;;) { - c = getChar(); - if (!IsHexDigit(c)) - break; - if (!literal_.append(c)) - return TOK_ERROR; - } - - putChar(c); - - if (!literal_.append('\0')) - return TOK_ERROR; - return TOK_HEX_LITERAL; -} - -TokenKind -Scanner::numberLiteral(char first) -{ - literal_.clear(); - if (!literal_.append(first)) - return TOK_ERROR; - - char c; - for (;;) { - c = getChar(); - if (!IsDigit(c)) - break; - if (!literal_.append(c)) - return TOK_ERROR; - } - - // Detect a hexadecimal string. - if (literal_.length() == 1 && - literal_[0] == '0' && - (c == 'x' || c == 'X')) - { - return hexLiteral(); - } - - if (c != '.') { - putChar(c); - if (!literal_.append('\0')) - return TOK_ERROR; - return TOK_INTEGER_LITERAL; - } - if (!literal_.append(c)) - return TOK_ERROR; - - c = getChar(); - if (!IsDigit(c)) { - cc_.reportError(begin(), Message_ExpectedDigitForFloat, c); - return TOK_ERROR; - } - if (!literal_.append(c)) - return TOK_ERROR; - - for (;;) { - c = getChar(); - if (!IsDigit(c)) - break; - if (!literal_.append(c)) - return TOK_ERROR; - } - - if (c != 'e') { - putChar(c); - } else { - if (!literal_.append(c)) - return TOK_ERROR; - c = getChar(); - if (c == '-') { - if (!literal_.append(c)) - return TOK_ERROR; - c = getChar(); - } - if (!IsDigit(c)) { - cc_.reportError(begin(), Message_ExpectedDigitForFloat, c); - return TOK_ERROR; - } - if (!literal_.append(c)) - return TOK_ERROR; - for (;;) { - c = getChar(); - if (!IsDigit(c)) { - putChar(c); - break; - } - if (!literal_.append(c)) - return TOK_ERROR; - } - } - - if (!literal_.append('\0')) - return TOK_ERROR; - - return TOK_FLOAT_LITERAL; -} - -static inline TokenKind -FindIdentifier(const char *str) -{ - if (!strcmp(str, "new")) - return TOK_NEW; - if (!strcmp(str, "for")) - return TOK_FOR; - if (!strcmp(str, "return")) - return TOK_RETURN; - if (!strcmp(str, "if")) - return TOK_IF; - if (!strcmp(str, "else")) - return TOK_ELSE; - if (!strcmp(str, "enum")) - return TOK_ENUM; - if (!strcmp(str, "while")) - return TOK_WHILE; - if (!strcmp(str, "do")) - return TOK_DO; - if (!strcmp(str, "break")) - return TOK_BREAK; - if (!strcmp(str, "continue")) - return TOK_CONTINUE; - if (!strcmp(str, "forward")) - return TOK_FORWARD; - if (!strcmp(str, "public")) - return TOK_PUBLIC; - if (!strcmp(str, "const")) - return TOK_CONST; - if (!strcmp(str, "native")) - return TOK_NATIVE; - if (!strcmp(str, "stock")) - return TOK_STOCK; - if (!strcmp(str, "true")) - return TOK_TRUE; - if (!strcmp(str, "false")) - return TOK_FALSE; - if (!strcmp(str, "struct")) - return TOK_STRUCT; - if (!strcmp(str, "switch")) - return TOK_SWITCH; - if (!strcmp(str, "case")) - return TOK_CASE; - if (!strcmp(str, "default")) - return TOK_DEFAULT; - if (!strcmp(str, "typedef")) - return TOK_TYPEDEF; - if (!strcmp(str, "void")) - return TOK_VOID; - if (!strcmp(str, "int")) - return TOK_INT; - if (!strcmp(str, "sizeof")) - return TOK_SIZEOF; - if (!strcmp(str, "import")) - return TOK_IMPORT; - if (!strcmp(str, "from")) - return TOK_FROM; - return TOK_NONE; -} - -TokenKind -Scanner::identifier(char first) -{ - literal_.clear(); - if (!literal_.append(first)) - return TOK_ERROR; - - char c; - for (;;) { - c = getChar(); - if (!isascii(c) || (!isalnum(c) && c != '_')) - break; - if (!literal_.append(c)) - return TOK_ERROR; - } - - if (!literal_.append('\0')) - return TOK_ERROR; - - // Most keywords do not act as labels. - TokenKind kind = FindIdentifier(literal()); - if (kind != TOK_NONE) { - putChar(c); - return kind; - } - - if (c == ':' && allowTags_) - return TOK_LABEL; - - putChar(c); - - // _ is a special case which acts as a tag and a keyword. - if (!strcmp(literal(), "_")) - return TOK_UNDERSCORE; - - return TOK_NAME; -} - -TokenKind -Scanner::next() -{ - assert(!pushed_); - - for (;;) { - begin_ = cur_; - - char c = getChar(); - switch (c) { - case '/': - { - c = getChar(); - if (c == '/') { - eatSingleLine(); - break; - } - if (c == '*') { - if (!eatComment()) { - cc_.reportError(begin(), Message_UnterminatedComment); - return TOK_ERROR; - } - break; - } - putChar(c); - return TOK_SLASH; - } - - case '\n': - case '\r': - calcNewLine(c); - break; - - case ';': return TOK_SEMICOLON; - case '{': return TOK_LBRACE; - case '}': return TOK_RBRACE; - case '(': return TOK_LPAREN; - case ')': return TOK_RPAREN; - case '[': return TOK_LBRACKET; - case ']': return TOK_RBRACKET; - case '~': return TOK_TILDE; - case '?': return TOK_QMARK; - case ':': return TOK_COLON; - - case '.': - c = getChar(); - if (c == '.') { - c = getChar(); - if (c == '.') - return TOK_ELLIPSES; - cc_.reportError(begin_, Message_UnexpectedCharacter, c, uint8(c)); - return TOK_ERROR; - } - putChar(c); - return TOK_DOT; - - case '*': - c = getChar(); - if (c == '=') - return TOK_ASSIGN_MUL; - putChar(c); - return TOK_STAR; - - case '+': - c = getChar(); - if (c == '=') - return TOK_ASSIGN_ADD; - if (c == '+') - return TOK_INCREMENT; - putChar(c); - return TOK_PLUS; - - case '&': - c = getChar(); - if (c == '=') - return TOK_ASSIGN_BITAND; - if (c == '&') - return TOK_AND; - putChar(c); - return TOK_BITAND; - - case '|': - c = getChar(); - if (c == '=') - return TOK_ASSIGN_BITOR; - if (c == '|') - return TOK_OR; - putChar(c); - return TOK_BITOR; - - case '^': - c = getChar(); - if (c == '=') - return TOK_ASSIGN_BITXOR; - putChar(c); - return TOK_BITXOR; - - case '%': - c = getChar(); - if (c == '=') - return TOK_ASSIGN_MOD; - putChar(c); - return TOK_PERCENT; - - case '-': - c = getChar(); - if (c == '=') - return TOK_ASSIGN_SUB; - if (c == '-') - return TOK_DECREMENT; - putChar(c); - return TOK_MINUS; - - case ',': return TOK_COMMA; - case '\0': return TOK_EOF; - - case '!': - c = getChar(); - if (c == '=') - return TOK_NOTEQUALS; - putChar(c); - return TOK_NOT; - - case '=': - c = getChar(); - if (c == '=') - return TOK_EQUALS; - putChar(c); - return TOK_ASSIGN; - - - case '<': - c = getChar(); - if (c == '=') - return TOK_LE; - if (c == '<') { - c = getChar(); - if (c == '=') - return TOK_ASSIGN_SHL; - putChar(c); - return TOK_SHL; - } - putChar(c); - return TOK_LT; - - case '>': - c = getChar(); - if (c == '=') - return TOK_GE; - if (c == '>') { - c = getChar(); - if (c == '>') { - c = getChar(); - if (c == '=') - return TOK_ASSIGN_USHR; - putChar(c); - return TOK_USHR; - } - return TOK_SHR; - } - putChar(c); - return TOK_GT; - - case ' ': - case '\t': - case '\f': - break; - - case '"': - return stringLiteral(); - - default: - if (c >= '0' && c <= '9') - return numberLiteral(c); - if (c == '_' || (isascii(c) && isalpha(c))) - return identifier(c); - cc_.reportError(begin_, Message_UnexpectedCharacter, c, uint8(c)); - return TOK_ERROR; - } - } -} - -TokenKind -Scanner::scan() -{ - if (pushed_) { - pushed_ = false; - return lastToken_; - } - lastToken_ = next(); - return lastToken_; -} - -TokenKind -Scanner::peek() -{ - if (pushed_) - return lastToken_; - scan(); - pushed_ = true; - return lastToken_; -} +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include +#include +#include +#include +#include +#include "../Utility.h" +#include "../Heap-inl.h" +#include "../Handles-inl.h" +#include "Scanner.h" + +using namespace ke; + +const char *ke::TokenNames[] = +{ +#define _(name, str) str, + TOKENMAP(_) +#undef _ + NULL +}; + +Scanner::Scanner(CompileContext &cc, const char *stream, size_t length) + : cc_(cc), + stream_(stream), + length_(length), + pushed_(false), + allowTags_(true), + saved_('\0'), + pos_(stream_) +{ + cur_.line = 1; + cur_.col = 1; + last_ = cur_; +} + +char +Scanner::getChar() +{ + if (saved_ != '\0') { + char temp = saved_; + saved_ = '\0'; + return temp; + } + if (pos_ >= stream_ + length_) + return '\0'; + last_.col = cur_.col++; + return *pos_++; +} + +void +Scanner::putChar(char c) +{ + assert(saved_ == '\0'); + saved_ = c; + cur_.col = last_.col; +} + +void +Scanner::calcNewLine(char c) +{ + cur_.line++; + cur_.col = 1; + last_ = cur_; + + char o; + if (c == '\r' && ((o = getChar()) != '\n')) + putChar(o); +} + +void +Scanner::eatSingleLine() +{ + char c; + while ((c = getChar()) != '\0') { + if (c == '\n' || c == '\r') { + calcNewLine(c); + break; + } + } +} + +bool +Scanner::eatComment() +{ + char c; + + while ((c = getChar()) != '\0') { + while (c == '*') { + c = getChar(); + if (c == '\0') + return false; + if (c == '/') + return true; + } + if (c == '\r' || c == '\n') + calcNewLine(c); + } + + return false; +} + +static inline bool +IsDigit(char c) +{ + return c >= '0' && c <= '9'; +} + +// Based off the logic in sc2.c's ftoi()... +double +Scanner::ParseDouble(const char *string) +{ + const char *ptr = string; + + double number = 0.0; + while (IsDigit(*ptr)) { + number = (number * 10) + (*ptr - '0'); + ptr++; + } + + assert(*ptr == '.'); + ptr++; + + double fraction = 0.0; + double multiplier = 1.0; + while (IsDigit(*ptr)) { + fraction = (fraction * 10) + (*ptr - '0'); + multiplier = multiplier / 10.0; + ptr++; + } + + number += fraction * multiplier; + + if (*ptr++ == 'e') { + int sign = 1; + if (*ptr == '-') { + sign = -1; + ptr++; + } + + int exponent = 0; + while (IsDigit(*ptr)) { + exponent = (exponent * 10) + (*ptr - '0'); + ptr++; + } + + multiplier = pow(10.0, exponent * sign); + number *= multiplier; + } + + return number; +} + +static inline bool +IsHexDigit(char c) +{ + return IsDigit(c) || + (c >= 'a' && c <= 'f') || + (c >= 'A' && c <= 'F'); +} + +// Based on the logic for litchar() in sc2.c. +int +Scanner::readEscapeCode() +{ + char c = getChar(); + if (c == '\\') + return c; + + switch (c) { + case 'a': + return '\a'; + case 'b': + return '\b'; + case 'e': + return 27; // Apparently \e is non-standard. + case 'f': + return '\f'; + case 'n': + return '\n'; + case 'r': + return '\r'; + case 't': + return '\t'; + case 'v': + return '\v'; + + case 'x': + { + unsigned digits = 0; + char r = 0; + + c = getChar(); + while (IsHexDigit(c) && digits < 2) { + if (IsDigit(c)) + c = (c << 4) + (c - '0'); + else + c = (c << 4) + (tolower(c) - 'a' + 10); + digits++; + c = getChar(); + } + + // Swallow a trailing ';' + if (c != ';') + putChar(c); + + return r; + } + + case '\'': + case '\"': + case '%': + return c; + + default: + { + if (IsDigit(c)) { + // \ddd + char r = 0; + while (IsDigit(c)) { + r = r * 10 + (c - '0'); + c = getChar(); + } + + // Swallow a trailing ; + if (c != ';') + putChar(c); + + return r; + } + cc_.reportError(begin(), Message_UnknownEscapeCode, c); + return INT_MAX; + } + } +} + +TokenKind +Scanner::stringLiteral() +{ + literal_.clear(); + + for (;;) { + char c = getChar(); + if (c == '\"') + break; + if (c == '\r' || c == '\n' || c == '\0') { + cc_.reportError(begin(), Message_UnterminatedString); + return TOK_ERROR; + } + if (c == '\\') { + int code = readEscapeCode(); + if (code == INT_MAX) + return TOK_ERROR; + c = char(code); + } + if (!literal_.append(c)) + return TOK_ERROR; + } + + if (!literal_.append('\0')) + return TOK_ERROR; + + return TOK_STRING_LITERAL; +} + +TokenKind +Scanner::hexLiteral() +{ + literal_.clear(); + + char c; + for (;;) { + c = getChar(); + if (!IsHexDigit(c)) + break; + if (!literal_.append(c)) + return TOK_ERROR; + } + + putChar(c); + + if (!literal_.append('\0')) + return TOK_ERROR; + return TOK_HEX_LITERAL; +} + +TokenKind +Scanner::numberLiteral(char first) +{ + literal_.clear(); + if (!literal_.append(first)) + return TOK_ERROR; + + char c; + for (;;) { + c = getChar(); + if (!IsDigit(c)) + break; + if (!literal_.append(c)) + return TOK_ERROR; + } + + // Detect a hexadecimal string. + if (literal_.length() == 1 && + literal_[0] == '0' && + (c == 'x' || c == 'X')) + { + return hexLiteral(); + } + + if (c != '.') { + putChar(c); + if (!literal_.append('\0')) + return TOK_ERROR; + return TOK_INTEGER_LITERAL; + } + if (!literal_.append(c)) + return TOK_ERROR; + + c = getChar(); + if (!IsDigit(c)) { + cc_.reportError(begin(), Message_ExpectedDigitForFloat, c); + return TOK_ERROR; + } + if (!literal_.append(c)) + return TOK_ERROR; + + for (;;) { + c = getChar(); + if (!IsDigit(c)) + break; + if (!literal_.append(c)) + return TOK_ERROR; + } + + if (c != 'e') { + putChar(c); + } else { + if (!literal_.append(c)) + return TOK_ERROR; + c = getChar(); + if (c == '-') { + if (!literal_.append(c)) + return TOK_ERROR; + c = getChar(); + } + if (!IsDigit(c)) { + cc_.reportError(begin(), Message_ExpectedDigitForFloat, c); + return TOK_ERROR; + } + if (!literal_.append(c)) + return TOK_ERROR; + for (;;) { + c = getChar(); + if (!IsDigit(c)) { + putChar(c); + break; + } + if (!literal_.append(c)) + return TOK_ERROR; + } + } + + if (!literal_.append('\0')) + return TOK_ERROR; + + return TOK_FLOAT_LITERAL; +} + +static inline TokenKind +FindIdentifier(const char *str) +{ + if (!strcmp(str, "new")) + return TOK_NEW; + if (!strcmp(str, "for")) + return TOK_FOR; + if (!strcmp(str, "return")) + return TOK_RETURN; + if (!strcmp(str, "if")) + return TOK_IF; + if (!strcmp(str, "else")) + return TOK_ELSE; + if (!strcmp(str, "enum")) + return TOK_ENUM; + if (!strcmp(str, "while")) + return TOK_WHILE; + if (!strcmp(str, "do")) + return TOK_DO; + if (!strcmp(str, "break")) + return TOK_BREAK; + if (!strcmp(str, "continue")) + return TOK_CONTINUE; + if (!strcmp(str, "forward")) + return TOK_FORWARD; + if (!strcmp(str, "public")) + return TOK_PUBLIC; + if (!strcmp(str, "const")) + return TOK_CONST; + if (!strcmp(str, "native")) + return TOK_NATIVE; + if (!strcmp(str, "stock")) + return TOK_STOCK; + if (!strcmp(str, "true")) + return TOK_TRUE; + if (!strcmp(str, "false")) + return TOK_FALSE; + if (!strcmp(str, "struct")) + return TOK_STRUCT; + if (!strcmp(str, "switch")) + return TOK_SWITCH; + if (!strcmp(str, "case")) + return TOK_CASE; + if (!strcmp(str, "default")) + return TOK_DEFAULT; + if (!strcmp(str, "typedef")) + return TOK_TYPEDEF; + if (!strcmp(str, "void")) + return TOK_VOID; + if (!strcmp(str, "int")) + return TOK_INT; + if (!strcmp(str, "sizeof")) + return TOK_SIZEOF; + if (!strcmp(str, "import")) + return TOK_IMPORT; + if (!strcmp(str, "from")) + return TOK_FROM; + return TOK_NONE; +} + +TokenKind +Scanner::identifier(char first) +{ + literal_.clear(); + if (!literal_.append(first)) + return TOK_ERROR; + + char c; + for (;;) { + c = getChar(); + if (!isascii(c) || (!isalnum(c) && c != '_')) + break; + if (!literal_.append(c)) + return TOK_ERROR; + } + + if (!literal_.append('\0')) + return TOK_ERROR; + + // Most keywords do not act as labels. + TokenKind kind = FindIdentifier(literal()); + if (kind != TOK_NONE) { + putChar(c); + return kind; + } + + if (c == ':' && allowTags_) + return TOK_LABEL; + + putChar(c); + + // _ is a special case which acts as a tag and a keyword. + if (!strcmp(literal(), "_")) + return TOK_UNDERSCORE; + + return TOK_NAME; +} + +TokenKind +Scanner::next() +{ + assert(!pushed_); + + for (;;) { + begin_ = cur_; + + char c = getChar(); + switch (c) { + case '/': + { + c = getChar(); + if (c == '/') { + eatSingleLine(); + break; + } + if (c == '*') { + if (!eatComment()) { + cc_.reportError(begin(), Message_UnterminatedComment); + return TOK_ERROR; + } + break; + } + putChar(c); + return TOK_SLASH; + } + + case '\n': + case '\r': + calcNewLine(c); + break; + + case ';': return TOK_SEMICOLON; + case '{': return TOK_LBRACE; + case '}': return TOK_RBRACE; + case '(': return TOK_LPAREN; + case ')': return TOK_RPAREN; + case '[': return TOK_LBRACKET; + case ']': return TOK_RBRACKET; + case '~': return TOK_TILDE; + case '?': return TOK_QMARK; + case ':': return TOK_COLON; + + case '.': + c = getChar(); + if (c == '.') { + c = getChar(); + if (c == '.') + return TOK_ELLIPSES; + cc_.reportError(begin_, Message_UnexpectedCharacter, c, uint8(c)); + return TOK_ERROR; + } + putChar(c); + return TOK_DOT; + + case '*': + c = getChar(); + if (c == '=') + return TOK_ASSIGN_MUL; + putChar(c); + return TOK_STAR; + + case '+': + c = getChar(); + if (c == '=') + return TOK_ASSIGN_ADD; + if (c == '+') + return TOK_INCREMENT; + putChar(c); + return TOK_PLUS; + + case '&': + c = getChar(); + if (c == '=') + return TOK_ASSIGN_BITAND; + if (c == '&') + return TOK_AND; + putChar(c); + return TOK_BITAND; + + case '|': + c = getChar(); + if (c == '=') + return TOK_ASSIGN_BITOR; + if (c == '|') + return TOK_OR; + putChar(c); + return TOK_BITOR; + + case '^': + c = getChar(); + if (c == '=') + return TOK_ASSIGN_BITXOR; + putChar(c); + return TOK_BITXOR; + + case '%': + c = getChar(); + if (c == '=') + return TOK_ASSIGN_MOD; + putChar(c); + return TOK_PERCENT; + + case '-': + c = getChar(); + if (c == '=') + return TOK_ASSIGN_SUB; + if (c == '-') + return TOK_DECREMENT; + putChar(c); + return TOK_MINUS; + + case ',': return TOK_COMMA; + case '\0': return TOK_EOF; + + case '!': + c = getChar(); + if (c == '=') + return TOK_NOTEQUALS; + putChar(c); + return TOK_NOT; + + case '=': + c = getChar(); + if (c == '=') + return TOK_EQUALS; + putChar(c); + return TOK_ASSIGN; + + + case '<': + c = getChar(); + if (c == '=') + return TOK_LE; + if (c == '<') { + c = getChar(); + if (c == '=') + return TOK_ASSIGN_SHL; + putChar(c); + return TOK_SHL; + } + putChar(c); + return TOK_LT; + + case '>': + c = getChar(); + if (c == '=') + return TOK_GE; + if (c == '>') { + c = getChar(); + if (c == '>') { + c = getChar(); + if (c == '=') + return TOK_ASSIGN_USHR; + putChar(c); + return TOK_USHR; + } + return TOK_SHR; + } + putChar(c); + return TOK_GT; + + case ' ': + case '\t': + case '\f': + break; + + case '"': + return stringLiteral(); + + default: + if (c >= '0' && c <= '9') + return numberLiteral(c); + if (c == '_' || (isascii(c) && isalpha(c))) + return identifier(c); + cc_.reportError(begin_, Message_UnexpectedCharacter, c, uint8(c)); + return TOK_ERROR; + } + } +} + +TokenKind +Scanner::scan() +{ + if (pushed_) { + pushed_ = false; + return lastToken_; + } + lastToken_ = next(); + return lastToken_; +} + +TokenKind +Scanner::peek() +{ + if (pushed_) + return lastToken_; + scan(); + pushed_ = true; + return lastToken_; +} diff -r c4b1341297e5 -r ba85a47ee414 src/compiler/Scanner.h --- a/src/compiler/Scanner.h Mon Jun 10 00:46:29 2013 +0100 +++ b/src/compiler/Scanner.h Mon Jun 10 01:04:43 2013 +0100 @@ -1,114 +1,114 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#ifndef _include_jitcraft_scanner_h_ -#define _include_jitcraft_scanner_h_ - -#include "Token.h" -#include "CompileContext.h" -#include "../Opcodes.h" -#include "../Vector.h" - -namespace ke { - -class Scanner -{ - template - friend class AutoAllowTags; - - CompileContext &cc_; - const char *stream_; - size_t length_; - bool pushed_; - bool allowTags_; - char saved_; - const char *pos_; - Vector literal_; - TokenKind lastToken_; - SourcePosition last_; - SourcePosition cur_; - SourcePosition begin_; - - private: - char getChar(); - void putChar(char c); - void eatSingleLine(); - bool eatComment(); - void calcNewLine(char c); - int readEscapeCode(); - TokenKind stringLiteral(); - TokenKind hexLiteral(); - TokenKind numberLiteral(char first); - TokenKind identifier(char first); - - TokenKind next(); - - public: - Scanner(CompileContext &cc, const char *stream, size_t length); - - static double ParseDouble(const char *string); - - TokenKind scan(); - TokenKind peek(); - TokenKind last() const { - return lastToken_; - } - - bool tagsEnabled() const { - return allowTags_; - } - void setTagsEnabled(bool enabled) { - allowTags_ = enabled; - } - - // Beginning source position of the last instruction. - SourcePosition begin() const { - return begin_; - } - - // Note: the literal may have embedded NUL characters. - const char *literal() const { - return literal_.buffer(); - } - size_t literal_length() const { - return literal_.length() - 1; - } -}; - -template -class AutoAllowTags -{ - Scanner &scanner_; - bool prev_; - - public: - AutoAllowTags(Scanner &scanner) - : scanner_(scanner), - prev_(scanner_.tagsEnabled()) - { - scanner_.setTagsEnabled(Enabled); - } - ~AutoAllowTags() - { - scanner_.setTagsEnabled(prev_); - } -}; - -} // namespace ke - -#endif // _include_jitcraft_scanner_h_ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_scanner_h_ +#define _include_jitcraft_scanner_h_ + +#include "Token.h" +#include "CompileContext.h" +#include "../Opcodes.h" +#include "../Vector.h" + +namespace ke { + +class Scanner +{ + template + friend class AutoAllowTags; + + CompileContext &cc_; + const char *stream_; + size_t length_; + bool pushed_; + bool allowTags_; + char saved_; + const char *pos_; + Vector literal_; + TokenKind lastToken_; + SourcePosition last_; + SourcePosition cur_; + SourcePosition begin_; + + private: + char getChar(); + void putChar(char c); + void eatSingleLine(); + bool eatComment(); + void calcNewLine(char c); + int readEscapeCode(); + TokenKind stringLiteral(); + TokenKind hexLiteral(); + TokenKind numberLiteral(char first); + TokenKind identifier(char first); + + TokenKind next(); + + public: + Scanner(CompileContext &cc, const char *stream, size_t length); + + static double ParseDouble(const char *string); + + TokenKind scan(); + TokenKind peek(); + TokenKind last() const { + return lastToken_; + } + + bool tagsEnabled() const { + return allowTags_; + } + void setTagsEnabled(bool enabled) { + allowTags_ = enabled; + } + + // Beginning source position of the last instruction. + SourcePosition begin() const { + return begin_; + } + + // Note: the literal may have embedded NUL characters. + const char *literal() const { + return literal_.buffer(); + } + size_t literal_length() const { + return literal_.length() - 1; + } +}; + +template +class AutoAllowTags +{ + Scanner &scanner_; + bool prev_; + + public: + AutoAllowTags(Scanner &scanner) + : scanner_(scanner), + prev_(scanner_.tagsEnabled()) + { + scanner_.setTagsEnabled(Enabled); + } + ~AutoAllowTags() + { + scanner_.setTagsEnabled(prev_); + } +}; + +} // namespace ke + +#endif // _include_jitcraft_scanner_h_ diff -r c4b1341297e5 -r ba85a47ee414 src/compiler/Scopes.cpp --- a/src/compiler/Scopes.cpp Mon Jun 10 00:46:29 2013 +0100 +++ b/src/compiler/Scopes.cpp Mon Jun 10 01:04:43 2013 +0100 @@ -1,194 +1,194 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of JITCraft. - * - * JITCraft is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * JITCraft. If not, see http://www.gnu.org/licenses/. - */ -#include "../Zone.h" -#include "../Types.h" -#include "../TypeManager.h" -#include "../Strings.h" -#include "../Environments.h" -#include "../Structures.h" -#include "CompileContext.h" -#include "Scopes.h" -#include "../Spaces-inl.h" -#include "../Heap-inl.h" - -using namespace ke; - -Scope::Scope(PoolAllocator &pool, Kind kind, Scope *enclosing) - : pool_(pool), - kind_(kind), - enclosing_(enclosing) -{ -} - -bool -Scope::initialize() -{ - if (enclosing_ && !enclosing_->children_.append(this)) - return false; - return true; -} - -void -Scope::unlink() -{ - assert(empty()); - - assert(enclosing_->children_[enclosing_->children_.length() - 1] == this); - enclosing_->children_.pop(); - - // OOM here is swallowed and checked after compilation. - // :TODO: make the above true. - for (size_t i = 0; i < children_.length(); i++) - enclosing_->children_.append(children_[i]); -} - -Scope * -Scope::unlinkIfEmpty() -{ - if (empty()) { - unlink(); - return NULL; - } - return this; -} - -Symbol * -Scope::localLookup(Handle name) -{ - for (size_t i = 0; i < names_.length(); i++) { - if (names_[i]->name() == name) - return names_[i]; - } - return NULL; -} - -Symbol * -Scope::lookup(Handle name) -{ - for (Scope *scope = this; scope; scope = scope->enclosing()) { - if (Symbol *bound = scope->localLookup(name)) - return bound; - } - return NULL; -} - -#if 0 -void -Scope::setUsesLifoHeap() -{ - // Only one lifo slot is needed per scope. -#if 0 - if (lifoSlot_) - return; -#endif - - // If the parent of this scope is the function scope, then this is the - // function's block scope, and the lifo slot is part of the Frame instead. - // Note we can get a function scope here if we're making a temporary in a - // function with no local variables. - if (kind() == FUNCTION || enclosing()->kind() == FUNCTION) - return; - - Local type(ZONE(), ZONE()->types()->getPrimitive(PrimitiveType_Native)); - if (!type) - return; - - Local name(ZONE()); -#if 0 - lifoSlot_ = new (pool_) Variable(this, name, SourcePosition()); - lifoSlot_->setType(type); -#endif -} -#endif - -bool -Scope::addSymbol(Symbol *sym) -{ - return names_.append(sym); -} - -LocalScope::LocalScope(PoolAllocator &pool) - : Scope(pool, Scope::BLOCK, NULL) -{ -} - -LocalScope * -LocalScope::New(PoolAllocator &pool) -{ - LocalScope *scope = new (pool) LocalScope(pool); - if (!scope->initialize()) - return NULL; - - return scope; -} - -ArgumentScope::ArgumentScope(PoolAllocator &pool) - : Scope(pool, Scope::FUNCTION, NULL) -{ -} - -ArgumentScope * -ArgumentScope::New(PoolAllocator &pool) -{ - ArgumentScope *scope = new (pool) ArgumentScope(pool); - if (!scope->initialize()) - return NULL; - - return scope; -} - -GlobalScope::GlobalScope(PoolAllocator &pool, ImportScope *parent) - : Scope(pool, Scope::GLOBAL, parent) -{ -} - -GlobalScope * -GlobalScope::New(PoolAllocator &pool, ImportScope *parent) -{ - GlobalScope *scope = new (pool) GlobalScope(pool, parent); - if (!scope->initialize()) - return NULL; - - return scope; -} - -Symbol * -GlobalScope::findExported(Handle name) -{ - for (size_t i = 0; i < exported_.length(); i++) { - if (exported_[i]->name() == name) - return exported_[i]; - } - return NULL; -} - -ImportScope::ImportScope(PoolAllocator &pool) - : Scope(pool, Scope::IMPORT, NULL) -{ -} - -ImportScope * -ImportScope::New(PoolAllocator &pool) -{ - ImportScope *scope = new (pool) ImportScope(pool); - if (!scope->initialize()) - return NULL; - - return scope; -} +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include "../Zone.h" +#include "../Types.h" +#include "../TypeManager.h" +#include "../Strings.h" +#include "../Environments.h" +#include "../Structures.h" +#include "CompileContext.h" +#include "Scopes.h" +#include "../Spaces-inl.h" +#include "../Heap-inl.h" + +using namespace ke; + +Scope::Scope(PoolAllocator &pool, Kind kind, Scope *enclosing) + : pool_(pool), + kind_(kind), + enclosing_(enclosing) +{ +} + +bool +Scope::initialize() +{ + if (enclosing_ && !enclosing_->children_.append(this)) + return false; + return true; +} + +void +Scope::unlink() +{ + assert(empty()); + + assert(enclosing_->children_[enclosing_->children_.length() - 1] == this); + enclosing_->children_.pop(); + + // OOM here is swallowed and checked after compilation. + // :TODO: make the above true. + for (size_t i = 0; i < children_.length(); i++) + enclosing_->children_.append(children_[i]); +} + +Scope * +Scope::unlinkIfEmpty() +{ + if (empty()) { + unlink(); + return NULL; + } + return this; +} + +Symbol * +Scope::localLookup(Handle name) +{ + for (size_t i = 0; i < names_.length(); i++) { + if (names_[i]->name() == name) + return names_[i]; + } + return NULL; +} + +Symbol * +Scope::lookup(Handle name) +{ + for (Scope *scope = this; scope; scope = scope->enclosing()) { + if (Symbol *bound = scope->localLookup(name)) + return bound; + } + return NULL; +} + +#if 0 +void +Scope::setUsesLifoHeap() +{ + // Only one lifo slot is needed per scope. +#if 0 + if (lifoSlot_) + return; +#endif + + // If the parent of this scope is the function scope, then this is the + // function's block scope, and the lifo slot is part of the Frame instead. + // Note we can get a function scope here if we're making a temporary in a + // function with no local variables. + if (kind() == FUNCTION || enclosing()->kind() == FUNCTION) + return; + + Local type(ZONE(), ZONE()->types()->getPrimitive(PrimitiveType_Native)); + if (!type) + return; + + Local name(ZONE()); +#if 0 + lifoSlot_ = new (pool_) Variable(this, name, SourcePosition()); + lifoSlot_->setType(type); +#endif +} +#endif + +bool +Scope::addSymbol(Symbol *sym) +{ + return names_.append(sym); +} + +LocalScope::LocalScope(PoolAllocator &pool) + : Scope(pool, Scope::BLOCK, NULL) +{ +} + +LocalScope * +LocalScope::New(PoolAllocator &pool) +{ + LocalScope *scope = new (pool) LocalScope(pool); + if (!scope->initialize()) + return NULL; + + return scope; +} + +ArgumentScope::ArgumentScope(PoolAllocator &pool) + : Scope(pool, Scope::FUNCTION, NULL) +{ +} + +ArgumentScope * +ArgumentScope::New(PoolAllocator &pool) +{ + ArgumentScope *scope = new (pool) ArgumentScope(pool); + if (!scope->initialize()) + return NULL; + + return scope; +} + +GlobalScope::GlobalScope(PoolAllocator &pool, ImportScope *parent) + : Scope(pool, Scope::GLOBAL, parent) +{ +} + +GlobalScope * +GlobalScope::New(PoolAllocator &pool, ImportScope *parent) +{ + GlobalScope *scope = new (pool) GlobalScope(pool, parent); + if (!scope->initialize()) + return NULL; + + return scope; +} + +Symbol * +GlobalScope::findExported(Handle name) +{ + for (size_t i = 0; i < exported_.length(); i++) { + if (exported_[i]->name() == name) + return exported_[i]; + } + return NULL; +} + +ImportScope::ImportScope(PoolAllocator &pool) + : Scope(pool, Scope::IMPORT, NULL) +{ +} + +ImportScope * +ImportScope::New(PoolAllocator &pool) +{ + ImportScope *scope = new (pool) ImportScope(pool); + if (!scope->initialize()) + return NULL; + + return scope; +} diff -r c4b1341297e5 -r ba85a47ee414 src/compiler/Scopes.h --- a/src/compiler/Scopes.h Mon Jun 10 00:46:29 2013 +0100 +++ b/src/compiler/Scopes.h Mon Jun 10 01:04:43 2013 +0100 @@ -1,125 +1,125 @@ -/* - * Copyright (C) 2012 David Anderson - * - * This file is part of JITCraft. - * - * JITCraft is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * JITCraft. If not, see http://www.gnu.org/licenses/. - */ -#ifndef _include_jitcraft_scopes_h_ -#define _include_jitcraft_scopes_h_ - -#include "../PoolAllocator.h" -#include "../Structures.h" -#include "AST.h" -#include "NameBinding.h" - -namespace ke { - -class CompileContext; -class EnvironmentMap; -class Struct; - -class Scope : public PoolObject -{ - protected: - PoolAllocator &pool_; - PoolList names_; - PoolList children_; - - bool empty() const { - return names_.length() == 0; - } - void unlink(); - - public: - enum Kind { - IMPORT, - GLOBAL, - FUNCTION, - BLOCK - }; - - public: - Scope(PoolAllocator &pool, Kind kind, Scope *enclosing); - bool initialize(); - - Kind kind() const { - return kind_; - } - Scope *enclosing() const { - return enclosing_; - } - - bool addSymbol(Symbol *symbol); - - Scope *unlinkIfEmpty(); - Symbol *localLookup(Handle name); - Symbol *lookup(Handle name); - - EnvironmentMap *createEnvironmentMap(Zone *zone); - - void setParent(Scope *parent) { - assert(!enclosing_ || enclosing_ == parent); - enclosing_ = parent; - } - - private: - Kind kind_; - Scope *enclosing_; -}; - -class ArgumentScope : public Scope -{ - public: - static ArgumentScope *New(PoolAllocator &pool); - - private: - ArgumentScope(PoolAllocator &pool); -}; - -class LocalScope : public Scope -{ - public: - static LocalScope *New(PoolAllocator &pool); - - private: - LocalScope(PoolAllocator &pool); -}; - -class GlobalScope : public Scope -{ - public: - static GlobalScope *New(PoolAllocator &pool, ImportScope *parent); - - PoolList *exported() { - return &exported_; - } - Symbol *findExported(Handle name); - - private: - GlobalScope(PoolAllocator &pool, ImportScope *parent); - PoolList exported_; -}; - -class ImportScope : public Scope -{ - public: - static ImportScope *New(PoolAllocator &pool); - - private: - ImportScope(PoolAllocator &pool); -}; - -} - -#endif // _include_jitcraft_scopes_h_ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_scopes_h_ +#define _include_jitcraft_scopes_h_ + +#include "../PoolAllocator.h" +#include "../Structures.h" +#include "AST.h" +#include "NameBinding.h" + +namespace ke { + +class CompileContext; +class EnvironmentMap; +class Struct; + +class Scope : public PoolObject +{ + protected: + PoolAllocator &pool_; + PoolList names_; + PoolList children_; + + bool empty() const { + return names_.length() == 0; + } + void unlink(); + + public: + enum Kind { + IMPORT, + GLOBAL, + FUNCTION, + BLOCK + }; + + public: + Scope(PoolAllocator &pool, Kind kind, Scope *enclosing); + bool initialize(); + + Kind kind() const { + return kind_; + } + Scope *enclosing() const { + return enclosing_; + } + + bool addSymbol(Symbol *symbol); + + Scope *unlinkIfEmpty(); + Symbol *localLookup(Handle name); + Symbol *lookup(Handle name); + + EnvironmentMap *createEnvironmentMap(Zone *zone); + + void setParent(Scope *parent) { + assert(!enclosing_ || enclosing_ == parent); + enclosing_ = parent; + } + + private: + Kind kind_; + Scope *enclosing_; +}; + +class ArgumentScope : public Scope +{ + public: + static ArgumentScope *New(PoolAllocator &pool); + + private: + ArgumentScope(PoolAllocator &pool); +}; + +class LocalScope : public Scope +{ + public: + static LocalScope *New(PoolAllocator &pool); + + private: + LocalScope(PoolAllocator &pool); +}; + +class GlobalScope : public Scope +{ + public: + static GlobalScope *New(PoolAllocator &pool, ImportScope *parent); + + PoolList *exported() { + return &exported_; + } + Symbol *findExported(Handle name); + + private: + GlobalScope(PoolAllocator &pool, ImportScope *parent); + PoolList exported_; +}; + +class ImportScope : public Scope +{ + public: + static ImportScope *New(PoolAllocator &pool); + + private: + ImportScope(PoolAllocator &pool); +}; + +} + +#endif // _include_jitcraft_scopes_h_ diff -r c4b1341297e5 -r ba85a47ee414 src/compiler/SemanticAnalysis.cpp --- a/src/compiler/SemanticAnalysis.cpp Mon Jun 10 00:46:29 2013 +0100 +++ b/src/compiler/SemanticAnalysis.cpp Mon Jun 10 01:04:43 2013 +0100 @@ -1,1423 +1,1423 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#include -#include "../Zone.h" -#include "../Strings.h" -#include "../NativeTable.h" -#include "../TypeManager.h" -#include "../Modules.h" -#include "../Code.h" -#include "SemanticAnalysis.h" -#include "../Heap-inl.h" - -using namespace ke; - -#define __ emitter_. - -typedef BytecodeEmitter::Label Label; - -class AutoEnterScope -{ - public: - AutoEnterScope(Scope **prevp, Scope *scope) - : prevp_(prevp), - prev_(*prevp), - scope_(scope) - { - if (!scope_) - return; - prev_ = *prevp_; - *prevp_ = scope; - assert(prev_ == scope->enclosing()); - } - ~AutoEnterScope() - { - if (scope_) - *prevp_ = prev_; - } - private: - Scope **prevp_; - Scope *prev_; - Scope *scope_; -}; - -SemanticAnalysis::SemanticAnalysis(Zone *zone, CompileContext &cc, TranslationUnit *unit) - : zone_(zone), - pool_(zone->pool()), - cc_(cc), - unit_(unit), - hir_(NULL), - loop_(NULL) -{ -} - -SemanticAnalysis::SemanticAnalysis(SemanticAnalysis *parent, Handle fun) - : zone_(parent->zone_), - pool_(parent->pool_), - cc_(parent->cc_), - unit_(parent->unit_), - hir_(NULL), - scope_(parent->scope_), - fun_(fun), - loop_(NULL), - control_(NULL) -{ - -} - -void -SemanticAnalysis::analyze() -{ - scope_ = unit_->globalScope(); - - for (size_t i = 0; i < unit_->tree()->statements()->length(); i++) { - Statement *stmt = unit_->tree()->statements()->at(i); - stmt->accept(this); - } - - Local code(zone_, __ finish()); - if (!code) - return; - code->setModule(unit_->module()); - unit_->module()->setCode(code); - -#ifndef NDEBUG -// Disassemble(code, stdout); -#endif -} - -void -SemanticAnalysis::visitFunctionStatement(FunctionStatement *stmt) -{ - FunctionSymbol *sym = stmt->sym(); - Local type(zone_, sym->type()); - - assert(scope_->kind() == Scope::GLOBAL); - - if (stmt->token() == TOK_NATIVE) { - // Define the native now. - size_t index; - if (!zone_->nativeTable()->add(stmt->name(), &index)) - return; - - Local native(zone_, Native::New(zone_, type, index)); - if (!native) - return; - - __ object(native); - __ setglobal(sym); - __ pop(); - } - - // If the function is a forward or native, we don't have to do anything. - if (stmt->token() == TOK_NATIVE || stmt->token() == TOK_FORWARD) - return; - - SemanticAnalysis child(this, type); - Local funobj(zone_, child.compile(stmt)); - if (!funobj) - return; - - __ object(funobj); - __ setglobal(sym); - if (stmt->token() == TOK_PUBLIC) - __ public_(); - else - __ pop(); -} - -Function * -SemanticAnalysis::compile(FunctionStatement *stmt) -{ - __ setFirstLine(stmt->pos().line); - - AutoEnterScope argScope(&scope_, stmt->argScope()); - - for (size_t i = 0; i < stmt->signature().parameters()->length(); i++) { - Parameter *param = stmt->signature().parameters()->at(i); - param->sym()->allocate(VariableSymbol::Arg, i); - } - - AutoEnterScope varScope(&scope_, stmt->varScope()); - ControlStatus status = visitForControl(stmt->body()); - - if (!fun_->returnType()->isVoid()) { - // If the function is not void, report if not all paths return a value. - // All other incorrect return cases have been caught already. - if (status.returns() == Returns_Sometimes) { - cc_.reportError(stmt->pos(), Message_NotAllPathsReturnValue); - return NULL; - } - - if (status.returns() == Returns_Never) { - cc_.reportError(stmt->pos(), Message_MissingReturnValue); - return NULL; - } - } - - Local code(zone_, __ finish()); - if (!code) - return NULL; - code->setModule(unit_->module()); - - Local fun(zone_, Function::New(zone_, code, fun_, stmt->name(), stmt->pos())); - if (!fun) - return NULL; - -#ifndef NDEBUG -// Disassemble(code, stdout); -#endif - - return fun; -} - -void -SemanticAnalysis::visitBlockStatement(BlockStatement *node) -{ - AutoEnterScope scope(&scope_, node->scope()); - - for (size_t i = 0; i < node->statements()->length(); i++) { - Statement *stmt = node->statements()->at(i); - __ note_position(stmt->pos()); - stmt->accept(this); - assert(__ stackDepth() == 0); - } -} - -void -SemanticAnalysis::visitEnumStatement(EnumStatement *node) -{ -} - -void -SemanticAnalysis::visitExpressionStatement(ExpressionStatement *node) -{ - // While building HIR, we enter new pool and root scopes, since HIR - // creation is purely temporary. - PoolScope poolScope(zone_->pool()); - RootScope rootScope(zone_); - - HIR *hir = eval(node->expression()); - if (!hir) - return; - - discharge(hir); - if (!hir->type()->isVoid()) - __ pop(); -} - -void -SemanticAnalysis::ControlScope::merge(const ControlStatus &left, const ControlStatus &right) -{ - // If both sides terminate in a return, we consider this a return as well. - if (left.returns() == Returns_Always && right.returns() == Returns_Always) { - terminate(Returns_Always); - return; - } - - ReturnStatus status = (left.returns() == Returns_Never && right.returns() == Returns_Never) - ? Returns_Never - : Returns_Sometimes; - - if (left.terminates() && right.terminates()) - terminate(status); - else - returns(status); -} - -SemanticAnalysis::ControlStatus -SemanticAnalysis::visitForControl(Statement *statement) -{ - ControlScope scope(&control_); - statement->accept(this); - return scope.status(); -} - -void -SemanticAnalysis::visitIfStatement(IfStatement *node) -{ - Label join; - - ControlStatus trueBranch; - ControlStatus falseBranch(false, Returns_Never); - - if (!node->ifFalse()) { - Label success; - - visitForTest(node->condition(), &success, &join, &success); - __ bind(&success); - trueBranch = visitForControl(node->ifTrue()); - } else { - Label success, next; - - visitForTest(node->condition(), &success, &next, &success); - __ bind(&success); - trueBranch = visitForControl(node->ifTrue()); - __ jump(&join); - __ bind(&next); - falseBranch = visitForControl(node->ifFalse()); - } - - __ bind(&join); - - control_->merge(trueBranch, falseBranch); -} - -void -SemanticAnalysis::requireLifoStack() -{ - if (!loop_) - return; - - loop_->setUsesLifoStack(); -} - -SemanticAnalysis::ControlStatus -SemanticAnalysis::visitLoopBody(Statement *stmt) -{ - // We always mark off a loop body, so on each edge, we can balance the - // LIFO stack. Right now, we don't have the ability to budget the LIFO - // stack ahead of time. If we turn out to never need the LIFO stack, we - // mark it as unnecessary. (The interpreter can't really make use of this, - // but the JIT can). - // - // Note that balancing the LIFO stack isn't even necessary, so this - // feature may be removed. All this mechanism does is mitigate the - // possibility that a long-running loop will overflow the LIFO region - // and therefore start requiring nursery allocations. - int pos = __ enterloop(); - ControlStatus status = visitForControl(stmt); - __ leaveloop(); - - if (loop_->usesLifoStack()) - __ setLoopUsesLifoStack(pos); - - return status; -} - -void -SemanticAnalysis::visitForStatement(ForStatement *node) -{ - AutoEnterScope scope(&scope_, node->scope()); - PoolScope poolScope(zone_->pool()); - RootScope rootScope(zone_); - - Label join, update; - LoopScope loop(scope_, &join, &update, &loop_); - - if (node->initialization()) - node->initialization()->accept(this); - - Label header, test; - if (node->condition()) - __ jump(&test); - - __ bind(&header); - ControlStatus status = visitLoopBody(node->body()); - - __ bind(&update); - if (node->update()) - node->update()->accept(this); - - if (node->condition()) { - __ bind(&test); - visitForTest(node->condition(), &header, &join, &join); - } else { - __ jump(&header); - } - - __ bind(&join); - - // If there was a condition, we treat this like a while loop. Otherwise, - // the join point is only reachable if the loop contains a break. The - // termination status doesn't tell us anything yet. - if (node->condition() || loop.breaks()) - control_->merge(status, ControlStatus(false, Returns_Never)); - else - control_->terminate(status.returns()); - - assert(__ stackDepth() == 0); -} - -void -SemanticAnalysis::visitWhileStatement(WhileStatement *node) -{ - PoolScope poolScope(zone_->pool()); - RootScope rootScope(zone_); - - Label header, join; - - __ bind(&header); - - ControlStatus status; - - LoopScope loop(scope_, &join, &header, &loop_); - if (node->token() == TOK_WHILE) { - Label body; - visitForTest(node->condition(), &body, &join, &body); - status = visitLoopBody(node->body()); - __ jump(&header); - } else { - status = visitLoopBody(node->body()); - visitForTest(node->condition(), &header, &join, &join); - } - - __ bind(&join); - - // Detect loops that will always have a fork in the contro-flow graph. - // This will be a |while| loop, or a |do-while| loop whose body does - // not terminate. For now, we treat these as an |if| regardless of the - // direction the branch can take. That means the control-flow is always - // conditional. - if (node->token() == TOK_WHILE || status.terminates() || loop.breaks()) { - control_->merge(status, ControlStatus(false, Returns_Never)); - } else { - // At this point, we've got a do-while loop with a |return| or - // |continue|, meaning that the rest of the block is unreachable. - control_->terminate(status.returns()); - } - - assert(__ stackDepth() == 0); -} - -static inline Heap::Tenure -InferTenure(Symbol *sym) -{ - if (sym->scope()->kind() == Scope::GLOBAL) - return Heap::Tenure_Old; - - // SP2's array semantics allow any array kind to escape, so we must use - // the normal heap. (Hopefully escape analysis can ease this pain later). - if (sym->type()->isArray()) - return Heap::Tenure_Default; - - return Heap::Tenure_Default; -} - -void -SemanticAnalysis::visitVariableDeclaration(VariableDeclaration *node) -{ - PoolScope poolScope(zone_->pool()); - RootScope rootScope(zone_); - - Local type(zone_, node->sym()->type()); - - if (node->sym()->type()->isVoid()) { - cc_.reportError(node->type()->pos(), Message_CannotDeclareVoid); - return; - } - - if (node->initialization()) { - HIR *hir = NULL; - - if ((hir = eval(node->initialization())) == NULL) - return; - if ((hir = coerce(hir, type, Coerce_Decl)) == NULL) - return; - - discharge(hir); - } else { - if (type->isInt32() || type->isEnum() || - type->isChar() || type->isBool()) - { - __ int_(0); - } else if (type->isFloat()) { - __ float_(0); - } else if (type->isArray()) { - Local atype(zone_, ArrayType::cast(type)); - Local map(zone_, atype->newMap()); - if (!map) - return; - - if (atype->isFixedLength()) { - __ newarray(OP_NEWFIXED, map, InferTenure(node->sym())); - } else if (node->dims()->at(0)) { - // This may not be true if we allow typedefing, so :TODO: - // figure out proper semantics. - assert(node->dims()->length() == atype->levels()); - - Local i32(zone_, zone_->types()->getPrimitive(PrimitiveType_Int32)); - for (size_t i = 0; i < node->dims()->length(); i++) { - Expression *expr = node->dims()->at(i); - if (expr) { - HIR *hir = eval(expr); - if (!hir || ((hir = coerce(hir, i32, Coerce_Arg)) == NULL)) - return; - discharge(hir); - } else { - __ int_(0); - } - } - __ newarray(OP_NEWSIZED, map, InferTenure(node->sym())); - } else { - __ newarray(OP_NEWEMPTY, map, InferTenure(node->sym())); - } - } else { - assert(false); - } - } - - if (node->sym()->scope()->kind() != Scope::GLOBAL) { - if (!__ allocate(node->sym())) - return; - } - - set(node->sym()); - __ pop(); -} - -void -SemanticAnalysis::visitReturnStatement(ReturnStatement *node) -{ - control_->returns(Returns_Always); - - if (!node->expression()) { - if (!fun_->returnType()->isVoid()) { - cc_.reportError(node->pos(), Message_MissingReturnValue); - return; - } - - __ returnvoid(); - return; - } - - if (fun_->returnType()->isVoid()) { - cc_.reportError(node->pos(), Message_UsedVoidReturn); - return; - } - - PoolScope poolScope(zone_->pool()); - RootScope rootScope(zone_); - - HIR *hir = eval(node->expression()); - if (!hir) - return; - - Local returnType(zone_, fun_->returnType()); - if ((hir = coerce(hir, returnType, Coerce_Return)) == NULL) - return; - - discharge(hir); - - if (hir->type()->isVoid()) - __ returnvoid(); - else - __ return_(); -} - -void -SemanticAnalysis::visitBinaryExpression(BinaryExpression *node) -{ - HIR *left = eval(node->left()); - HIR *right = eval(node->right()); - if (!left || !right) - return; - - hir_ = binary(node, node->token(), left, right); -} - -bool -SemanticAnalysis::checkArgumentCount(Handle type, unsigned actual) -{ - if (type->parameters()->length() == actual) - return true; - - if (actual > type->parameters()->length()) { - if (!type->isNative() || !type->isNativeVariadic()) - return false; - return true; - } - - if (!type->defaults()) - return false; - - // Missing arguments. See if there's a default argument for each missing - // actual. - for (unsigned i = actual; i < type->parameters()->length(); i++) { - if (!type->defaults()->at(i)) - return false; - } - - return true; -} - -static unsigned -BindImplicitName(Module *from, ImplicitSymbol *sym) -{ - if (sym->bound()) - return sym->fieldIndex(); - - Module *module = Module::cast(from->getImportAt(sym->index())); - StructMap *map = module->globals()->map(); - - unsigned fieldIndex; - Descriptor *desc = map->type()->lookupField(sym->name(), &fieldIndex); - assert(desc && desc->type() == sym->type()); - - sym->bind(fieldIndex); - return fieldIndex; -} - -void -SemanticAnalysis::visitNameProxy(NameProxy *proxy) -{ - Symbol *sym = proxy->sym(); - - switch (sym->kind()) { - case Symbol::kImport: - { - ImportSymbol *import = sym->toImport(); - hir_ = new (pool_) HImport(proxy, sym->type(), import->index()); - break; - } - - case Symbol::kImplicit: - { - ImplicitSymbol *import = sym->toImplicit(); - unsigned fieldIndex = BindImplicitName(unit_->module(), import); - - hir_ = new (pool_) HImport(proxy, zone_->types()->getImportable(), import->index()); - hir_ = new (pool_) HField(proxy, hir_, sym->type(), fieldIndex); - break; - } - - case Symbol::kConstant: - { - ConstantSymbol *val = sym->toConstant(); - hir_ = new (pool_) HInteger(proxy, sym->type(), val->value().toInt()); - break; - } - - // This is kind of screwy. Symbol::kFunction is like a constant we can't - // evaluate yet. Maybe we should extract the GLOBAL case and say that - // kFunction is only used for global functions (and use const vars for - // local functions). - case Symbol::kFunction: - case Symbol::kVariable: - { - Scope *in = sym->scope(); - switch (in->kind()) { - case Scope::GLOBAL: - hir_ = new (pool_) HGlobal(proxy, sym); - break; - - case Scope::FUNCTION: - { - VariableSymbol *var = sym->toVariable(); - assert(var->storage() == VariableSymbol::Arg); - hir_ = new (pool_) HLocal(proxy, var); - break; - } - - default: - { - VariableSymbol *var = sym->toVariable(); - assert(in->kind() == Scope::BLOCK); - assert(var->storage() == VariableSymbol::Local); - hir_ = new (pool_) HLocal(proxy, var); - break; - } - } - break; - } - - default: - // :TODO: - assert(false); - } -} - -void -SemanticAnalysis::visitBooleanLiteral(BooleanLiteral *node) -{ - Local type(zone_, zone_->types()->getPrimitive(PrimitiveType_Bool)); - hir_ = new (pool_) HBoolean(node, type, node->token() == TOK_TRUE); -} - -void -SemanticAnalysis::visitIntegerLiteral(IntegerLiteral *lit) -{ - Local type(zone_, zone_->types()->getPrimitive(PrimitiveType_Int32)); - hir_ = new (pool_) HInteger(lit, type, lit->value()); -} - -void -SemanticAnalysis::visitFloatLiteral(FloatLiteral *node) -{ - Local type(zone_, zone_->types()->getPrimitive(PrimitiveType_Float)); - hir_ = new (pool_) HFloat(node, type, (float)node->value()); -} - -void -SemanticAnalysis::visitStringLiteral(StringLiteral *node) -{ - Type *type = zone_->types()->getString(); - hir_ = new (pool_) HString(node, type, node->string()); -} - -void -SemanticAnalysis::visitCallExpression(CallExpression *node) -{ - HIR *callee = eval(node->callee()); - if (!callee) - return; - - if (!callee->type()->isFunction()) { - cc_.reportError(node->pos(), Message_CalleeNotFunction); - return; - } - - Local fun(zone_, FunctionType::cast(callee->type())); - - if (!checkArgumentCount(fun, node->arguments()->length())) { - cc_.reportError(node->pos(), Message_ArgumentCountMismatch); - return; - } - - if (fun->isForward()) { - cc_.reportError(node->pos(), Message_ForwardNotImplemented, fun->name()->chars()); - return; - } - - HIRList *args = new (pool_) HIRList; - for (unsigned i = 0; i < node->arguments()->length(); i++) { - Expression *expression = node->arguments()->at(i); - HIR *arg = eval(expression); - if (!arg) - return; - - Handle given = arg->type(); - - bool mustComputeReference; - if (i >= fun->parameters()->length()) { - // Special handling for natives. Arguments past the formal must - // always have references computed. However, arrays are already - // references for natives. - assert(fun->isNative() && fun->isNativeVariadic()); - mustComputeReference = !given->isReference() && - !given->isArray(); - } else { - Local actual(zone_, fun->parameterAt(i)); - - if (expression->isIndexExpression() && arg->isIndex()) { - // If the expression is the form x[y], and this produced a - // normal array indexing operation, then we apply some - // special rules. - - if (actual->isReference()) { - // Disabled, since this doesn't have easily definable or - // sensible semantics with SP2 arrays, but we don't want - // to silently break SP1 semantics. - cc_.reportError(expression->pos(), Message_CannotComputeIndexRef); - return; - } - - // As a special rule to account for SP1 semantics, we allow x[y] - // to automatically compute a sliced, read-only array view if - // the desired type is an array of one more dimension than x[y] - // would return. - // - // As an additional restriction, this is only allowed for - // one-level char arrays, to minimize potential issues until - // we determine whether more compatibility is needed. - // - // Internally, for sanity, the dependent array operation does - // not necessarily create a dependent array. For extensible - // arrays, we instead create a duplicate arrary of just the - // sliced portion, rather than deal with the complexity of the - // source array being resized. - // - // Note that we don't really know whether a dependent array - // will be dependent at compile-time, so the dependent type's - // just inherits from the source. - if (actual->isArray()) { - HIndex *index = arg->toIndex(); - Local basetype(zone_, ArrayType::cast(index->base()->type())); - Local atype(zone_, ArrayType::cast(actual)); - - if (atype->isCharArray() && basetype->isCharArray()) { - // There is no possible way this will type check, so - // it's safe to just try something completely - // different. - Local slicetype(zone_); - if (basetype->isFixedLength()) { - // If the base type is fixed length, we need to - // compute a new dynamic array type based on the - // fixed array's contents and qualifiers. - Local contained(zone_, basetype->contained()); - slicetype = zone_->types()->newArray(contained, ArrayType::DYNAMIC_ARRAY_SIZE); - if (!slicetype) - return; - slicetype = zone_->types()->qualify(slicetype, basetype->quals()); - if (!slicetype) - return; - } else { - slicetype = basetype; - } - arg = new (pool_) HNewDependentArray(expression, slicetype, index->base(), index->index()); - given = arg->type(); - } - } - } - - if (actual->isReference()) { - // If the actual is a reference, the coercion rules are much - // stricter; the contained types must be identical. - // :TODO: make this not an assert - assert(ReferenceType::cast(actual)->contained() == arg->type()); - } else { - // When we start overloading, coercion will have to happen later. - arg = coerce(arg, actual, Coerce_Arg); - if (!arg) - return; - } - - mustComputeReference = actual->isReference() && !given->isReference(); - } - - if (mustComputeReference) { - // Refs always use the LIFO stack if they can. - arg = new (pool_) HToRef(arg->node(), arg->type(), arg); - requireLifoStack(); - } - - if (!args->append(arg)) - return; - } - - hir_ = new (pool_) HCall(node, fun->returnType(), callee, args); -} - -void -SemanticAnalysis::visitAssignment(Assignment *node) -{ - HLValue *lval = lvalue(node->lvalue()); - if (!lval) - return; - - if (lval->type()->quals() & TypeQual_Const) { - cc_.reportError(node->pos(), Message_CannotModifyConst); - return; - } - - HIR *hir = eval(node->expression()); - if (!hir) - return; - if ((hir = coerce(hir, lval->type(), Coerce_Assign)) == NULL) - return; - - hir_ = new (pool_) HStore(node, node->token(), lval, hir); -} - -void -SemanticAnalysis::visitBreakStatement(BreakStatement *node) -{ - if (!loop_) { - cc_.reportError(node->pos(), Message_BreakOutsideLoop); - return; - } - - loop_->setBreaks(); - __ j(OP_BREAK, loop_->break_()); -} - -void -SemanticAnalysis::visitContinueStatement(ContinueStatement *node) -{ - if (!loop_) { - cc_.reportError(node->pos(), Message_ContinueOutsideLoop); - return; - } - - __ j(OP_CONTINUE, loop_->continue_()); -} - -void -SemanticAnalysis::visitIndexExpression(IndexExpression *node) -{ - HIR *left = eval(node->left()); - if (!left) - return; - - HIR *right = eval(node->right()); - if (!right) - return; - - // Hrm... yes, for now, let's coerce here. - Local i32(zone_, zone_->types()->getPrimitive(PrimitiveType_Int32)); - if ((right = coerce(right, i32, Coerce_Arg)) == NULL) - return; - - // Do not coerce here, there's no reason to yet. - if (!left->type()->isArray()) { - cc_.reportError(node->left()->pos(), Message_IndexBaseMustBeArray); - return; - } - - hir_ = new (pool_) HIndex(node, - ArrayType::cast(left->type())->contained(), - left, - right); -} - -void -SemanticAnalysis::visitIncDecExpression(IncDecExpression *node) -{ - HLValue *lval = lvalue(node->expression()); - if (!lval) - return; - - if (!lval->type()->isInt32() && !lval->type()->isFloat()) { - assert(false); - return; - } - - HIR *rval; - if (lval->type()->isInt32()) - rval = new (pool_) HInteger(node, lval->type(), 1); - else - rval = new (pool_) HFloat(node, lval->type(), 1.0); - - if (node->postfix()) - hir_ = new (pool_) HPostIncDec(node, node->token(), lval, rval); - else - hir_ = new (pool_) HStore(node, node->token(), lval, rval); -} - -Type * -SemanticAnalysis::coercionType(TokenKind token, HIR *left, HIR *right) -{ - Local l(zone_, left->type()); - Local r(zone_, right->type()); - - if (l->isPod()) - l = l->unqualified(); - if (r->isPod()) - r = r->unqualified(); - - if ((token >= TOK_PLUS && token <= TOK_SLASH) || - (token >= TOK_EQUALS && token <= TOK_GE)) - { - // We allow: [float|int32] + [float|int32] - if (l->isFloat() && r->isInt32()) - return l; - if (l->isInt32() && r->isFloat()) - return r; - if (l->isInt32() && r->isInt32()) - return l; - - if (l->isChar() && r->isInt32()) - return r; - if (l->isInt32() && r->isChar()) - return l; - } - - // :TODO: remove - assert(*l == *r); - - return left->type(); -} - -HIR * -SemanticAnalysis::typeCheckArray(const SourcePosition &pos, - HIR *from, - Handle argActual, - CoercionKind kind) -{ - Local argGiven(zone_, from->type()); - - // Special case: if we receive a string literal, and this is a char array, - // we allow a coercion. - if (from->isString() && - argGiven->isString() && - argActual->isArray() && - ArrayType::cast(argActual)->isCharArray()) - { - HString *hir = from->toString(); - Local actual(zone_, ArrayType::cast(argActual)); - - if (actual->isFixedLength()) { - if (hir->string()->length() + 1 > actual->fixedLength()) { - cc_.reportError(pos, Message_ArrayAssignmentTooBig); - return NULL; - } - } - - // Transform the string literal into an array literal. We give it the - // type of the left-hand side. The constness of the destination type - // will determine whether HArray duplicates it or not. - Local map(zone_, actual->newMap()); - if (!map) - return NULL; - - Local array(zone_); - array = Array::NewString(zone_, - map, - hir->string()->chars(), - hir->string()->length(), - Heap::Tenure_Old); - if (!array) - return NULL; - - // Since we're creating a canonical copy of the char array, we have to - // be careful that it doesn't get mutated. There are two cases where - // we know it can't be mutated, and therefore don't need to specify - // that it be cloned after pushing it onto the stack. - // - // (1) The array type is const. - // (2) The array type is fixed-length, and the coercion is for - // assignment, meaning it will be immediately copied. - // - // We treat declaration separately from assignment, since for - // declaration we can just immediately use the cloned version. - bool shouldDuplicate = true; - if ((actual->quals() & TypeQual_Const) || - (actual->isFixedLength() && kind == Coerce_Assign)) - { - shouldDuplicate = false; - } - - return new (pool_) HArray(hir->node(), actual, array, shouldDuplicate); - } - - if (!argGiven->isArray()) { - cc_.reportError(pos, Message_ExpectedArray); - return NULL; - } - - Local actual(zone_, ArrayType::cast(argActual)); - Local given(zone_, ArrayType::cast(argGiven)); - - if (actual->levels() != given->levels()) { - cc_.reportError(pos, Message_ArrayDimensionsMismatch); - return NULL; - } - - unsigned level = 1; - while (true) { - if (actual->isFixedLength()) { - if (!given->isFixedLength()) { - cc_.reportError(pos, Message_ArrayDimensionSizeMismatch, level); - return NULL; - } - - if (actual->isCharArray()) { - // String assignment can have a truncated |given|. - if (given->fixedLength() > actual->fixedLength()) { - cc_.reportError(pos, Message_ArrayAssignmentTooBig); - return NULL; - } - } else { - if (given->fixedLength() != actual->fixedLength()) { - cc_.reportError(pos, Message_ArrayDimensionSizeMismatch, level); - return NULL; - } - } - } else if (given->isFixedLength()) { - // We do not allow assignments in the form: - // [][] = [][Y] - // - // Otherwise, the following code could break the size guarantees - // of the right-hand side: - // - // new x[][5]; - // new y[][]; - // new z[3]; - // - // - // y = x; - // y[0] = z; - // - // Now |x[0]| contains a 3-length array. - if (level > 1) { - cc_.reportError(pos, Message_ArrayDimensionSizeMismatch, level); - return NULL; - } - } - - if (!given->contained()->isArray() || !actual->contained()->isArray()) - break; - - given = ArrayType::cast(given->contained()); - actual = ArrayType::cast(actual->contained()); - } - - bool givenConst = !!(given->contained()->quals() & TypeQual_Const); - bool actualConst = !!(actual->contained()->quals() & TypeQual_Const); - - // These arrays must contain the same innermost type. - if (given->contained()->unqualified() != actual->contained()->unqualified() || - (givenConst && !actualConst)) - { - char aname[255]; - char gname[255]; - cc_.reportError(pos, Message_TypeMismatch, - GetTypeName(argActual, aname, sizeof(aname)), - GetTypeName(argGiven, gname, sizeof(gname))); - return NULL; - } - - return from; -} - -HIR * -SemanticAnalysis::typeFailure(const SourcePosition &pos, Handle from, Handle to) -{ - char fname[255]; - char tname[255]; - cc_.reportError(pos, Message_InvalidCoercion, - GetTypeName(from, fname, sizeof(fname)), - GetTypeName(to, tname, sizeof(tname))); - return NULL; -} - -HIR * -SemanticAnalysis::coerce(HIR *hir, Handle to, CoercionKind kind) -{ - Local from(zone_, hir->type()); - - // If the source is *ever* an importable, we just fail. - if (from->isImportable()) - return typeFailure(hir->node()->pos(), from, to); - - if (from->isInt32() && to->isFloat()) - return new (pool_) HConvert(hir->node(), to, OP_CVT_I2F, hir); - if (from->isInt32() && to->isBool()) - return new (pool_) HConvert(hir->node(), to, OP_CVT_I2B, hir); - - if (to->isEnum() && from->unqualified() != to->unqualified()) - return typeFailure(hir->node()->pos(), from, to); - - if (to->isArray()) { - Local atype(zone_, ArrayType::cast(to)); - return typeCheckArray(hir->node()->pos(), hir, atype, kind); - } - - return hir; -} - -void -SemanticAnalysis::set(VariableSymbol *sym) -{ - if (sym->scope()->kind() == Scope::GLOBAL) { - __ setglobal(sym); - } else { - assert(sym->storage() == VariableSymbol::Local); - __ setlocal(sym); - } -} - -HIR * -SemanticAnalysis::binary(AstNode *node, TokenKind token, HIR *left, HIR *right) -{ - Local coercion(zone_, coercionType(token, left, right)); - if (!coercion) - return NULL; - if ((left = coerce(left, coercion, Coerce_Arg)) == NULL) - return NULL; - if ((right = coerce(right, coercion, Coerce_Arg)) == NULL) - return NULL; - - switch (token) { - case TOK_PLUS: - case TOK_MINUS: - case TOK_STAR: - case TOK_SLASH: - case TOK_PERCENT: - case TOK_AMPERSAND: - case TOK_BITOR: - case TOK_BITXOR: - case TOK_SHR: - case TOK_USHR: - case TOK_SHL: - return new (pool_) HBinary(node, left->type(), token, left, right); - - case TOK_EQUALS: - case TOK_NOTEQUALS: - case TOK_LT: - case TOK_LE: - case TOK_GT: - case TOK_GE: - case TOK_AND: - case TOK_OR: - { - Local result(zone_, zone_->types()->getPrimitive(PrimitiveType_Bool)); - return new (pool_) HBinary(node, result, token, left, right); - } - - default: - assert(false); - return NULL; - } -} - -static inline bool -IsValidUncheckedType(Type *type) -{ - return type->isEnum() || - type->isFloat() || - type->isInt32() || - type->isChar() || - type->isUnchecked() || - type->isBool(); -} - -void -SemanticAnalysis::visitUnaryExpression(UnaryExpression *node) -{ - HIR *hir = eval(node->expression()); - if (!hir) - return; - - switch (node->token()) { - case TOK_TILDE: - { - if (!hir->type()->isInt32() && !hir->type()->isEnum()) { - char name[255]; - cc_.reportError(node->pos(), Message_InvalidUnaryType, - "~", - GetTypeName(hir->type(), name, sizeof(name))); - } - Local type(zone_, zone_->types()->getPrimitive(PrimitiveType_Int32)); - hir_ = new (pool_) HUnary(node, hir->type(), TOK_TILDE, hir); - return; - } - - case TOK_MINUS: - { - if (hir->type()->isInt32() || hir->type()->isEnum()) { - Local type(zone_, zone_->types()->getPrimitive(PrimitiveType_Int32)); - hir_ = new (pool_) HUnary(node, type, TOK_MINUS, hir); - return; - } - if (hir->type()->isFloat()) { - hir_ = new (pool_) HUnary(node, hir->type(), TOK_MINUS, hir); - return; - } - - char name[255]; - cc_.reportError(node->pos(), Message_InvalidUnaryType, - "-", - GetTypeName(hir->type(), name, sizeof(name))); - break; - } - - default: - { - assert(node->token() == TOK_LABEL); - - TypeSymbol *sym = node->tag()->sym()->asType(); - if (!sym) { - cc_.reportError(node->pos(), Message_IdentifierIsNotAType, - sym->name()->chars()); - return; - } - - if (!IsValidUncheckedType(sym->effective())) { - char name[255]; - cc_.reportError(node->pos(), Message_InvalidUncheckedCastTo, - GetTypeName(sym->type(), name, sizeof(name))); - return; - } - if (!IsValidUncheckedType(hir->type())) { - char name[255]; - cc_.reportError(node->pos(), Message_InvalidUncheckedCastFrom, - GetTypeName(hir->type(), name, sizeof(name))); - return; - } - - hir_ = new (pool_) HUnary(node, sym->effective(), TOK_LABEL, hir); - break; - } - } -} - -void -SemanticAnalysis::import(HIR *hir, FieldExpression *node) -{ - // Note that we don't care if the hir type is "importable", since we - // can only do anything if the important has a constant binding. - unsigned index = hir->toImport()->index(); - Local importable(zone_, unit_->module()->getImportAt(index)); - - if (importable->isPackage()) { - // Look at what's already loaded and see if anything exists. We - // can't create stuff on-demand here since it's too late to add - // new translation units to the CompileContext. - Local package(zone_, Package::cast(importable)); - Local found(zone_, package->import(node->field())); - if (!found) { - cc_.reportError(node->pos(), Message_PackageDoesNotHaveMember, node->field()->chars()); - return; - } - - // If we did find something, make sure it is in our import list. - // If we didn't explicitly import it, we have no right to use it. - for (index = 0; index < unit_->module()->imports(); index++) { - if (unit_->module()->getImportAt(index) == importable) - break; - } - - if (index == unit_->module()->imports()) { - cc_.reportError(node->pos(), Message_ImportWasNeverImported, node->field()->chars()); - return; - } - - // Just create a new HImport. - hir_ = new (pool_) HImport(node, hir->type(), index); - } else { - // Modules can never have sub-modules or sub-packages. - unsigned index; - Local module(zone_, Module::cast(importable)); - Local map(zone_, module->globals()->map()); - Local desc(zone_, map->type()->lookupField(node->field(), &index)); - if (!desc) { - cc_.reportError(node->pos(), Message_FieldNotFound, node->field()->chars()); - return; - } - hir_ = new (pool_) HField(node, hir, desc->type(), index); - } -} - -void -SemanticAnalysis::visitFieldExpression(FieldExpression *node) -{ - HIR *hir = eval(node->left()); - if (!hir) - return; - - if (hir->isImport()) { - import(hir, node); - return; - } - - char name[255]; - cc_.reportError(node->pos(), Message_InvalidFieldExpression, - GetTypeName(hir->type(), name, sizeof(name))); -} - -static inline TokenKind -ToCompare(Expression *expr) -{ - if (expr->isBinaryExpression()) { - BinaryExpression *b = expr->toBinaryExpression(); - if (b->token() >= TOK_EQUALS && b->token() <= TOK_OR) - return b->token(); - } else if (expr->isUnaryExpression()) { - UnaryExpression *u = expr->toUnaryExpression(); - if (u->token() == TOK_NOT) - return TOK_NOT; - } - return TOK_EOF; -} - -void -SemanticAnalysis::visitForTest(Expression *expr, - Label *trueBranch, - Label *falseBranch, - Label *fallthrough) -{ - Local boolType(zone_, zone_->types()->getPrimitive(PrimitiveType_Bool)); - - TokenKind token = ToCompare(expr); - if (token == TOK_EOF || token == TOK_NOT) { - HIR *hir; - if (token == TOK_EOF) - hir = eval(expr); - else - hir = eval(expr->toUnaryExpression()->expression()); - if (!hir) - return; - if ((hir = coerce(hir, boolType, Coerce_Arg)) == NULL) - return; - - discharge(hir); - - bool jumpOnTrue = (fallthrough == falseBranch) ^ (token == TOK_NOT); - Opcode op = jumpOnTrue ? OP_JT : OP_JF; - - if (fallthrough == falseBranch) - __ j(op, trueBranch); - else - __ j(op, falseBranch); - return; - } - - BinaryExpression *bin = expr->toBinaryExpression(); - if (token == TOK_AND || token == TOK_OR) { - Label next; - if (token == TOK_AND) - visitForTest(bin->left(), &next, falseBranch, &next); - else - visitForTest(bin->left(), trueBranch, &next, &next); - __ bind(&next); - visitForTest(bin->right(), trueBranch, falseBranch, fallthrough); - return; - } - - HIR *left = eval(bin->left()); - HIR *right = eval(bin->right()); - if (!left || !right) - return; - - HIR *hir = binary(bin, bin->token(), left, right); - if (!hir) - return; - if ((hir = coerce(hir, boolType, Coerce_Arg)) == NULL) - return; - - discharge(hir); - if (fallthrough == trueBranch) - __ j(OP_JF, falseBranch); - else - __ j(OP_JT, trueBranch); -} - -HLValue * -SemanticAnalysis::lvalue(Expression *expr) -{ - HIR *hir = eval(expr); - if (!hir) - return NULL; - - if (!hir->isLValue()) { - cc_.reportError(expr->pos(), Message_ExpectedLValue); - return NULL; - } - - return static_cast(hir); -} - -HIR * -SemanticAnalysis::eval(Expression *expr) -{ - assert(!hir_); - expr->accept(this); - - HIR *hir = hir_; - hir_ = NULL; - - return hir; -} - -void -SemanticAnalysis::discharge(HIR *hir) -{ - EmitHIR(zone_, emitter_, hir); -} +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#include +#include "../Zone.h" +#include "../Strings.h" +#include "../NativeTable.h" +#include "../TypeManager.h" +#include "../Modules.h" +#include "../Code.h" +#include "SemanticAnalysis.h" +#include "../Heap-inl.h" + +using namespace ke; + +#define __ emitter_. + +typedef BytecodeEmitter::Label Label; + +class AutoEnterScope +{ + public: + AutoEnterScope(Scope **prevp, Scope *scope) + : prevp_(prevp), + prev_(*prevp), + scope_(scope) + { + if (!scope_) + return; + prev_ = *prevp_; + *prevp_ = scope; + assert(prev_ == scope->enclosing()); + } + ~AutoEnterScope() + { + if (scope_) + *prevp_ = prev_; + } + private: + Scope **prevp_; + Scope *prev_; + Scope *scope_; +}; + +SemanticAnalysis::SemanticAnalysis(Zone *zone, CompileContext &cc, TranslationUnit *unit) + : zone_(zone), + pool_(zone->pool()), + cc_(cc), + unit_(unit), + hir_(NULL), + loop_(NULL) +{ +} + +SemanticAnalysis::SemanticAnalysis(SemanticAnalysis *parent, Handle fun) + : zone_(parent->zone_), + pool_(parent->pool_), + cc_(parent->cc_), + unit_(parent->unit_), + hir_(NULL), + scope_(parent->scope_), + fun_(fun), + loop_(NULL), + control_(NULL) +{ + +} + +void +SemanticAnalysis::analyze() +{ + scope_ = unit_->globalScope(); + + for (size_t i = 0; i < unit_->tree()->statements()->length(); i++) { + Statement *stmt = unit_->tree()->statements()->at(i); + stmt->accept(this); + } + + Local code(zone_, __ finish()); + if (!code) + return; + code->setModule(unit_->module()); + unit_->module()->setCode(code); + +#ifndef NDEBUG +// Disassemble(code, stdout); +#endif +} + +void +SemanticAnalysis::visitFunctionStatement(FunctionStatement *stmt) +{ + FunctionSymbol *sym = stmt->sym(); + Local type(zone_, sym->type()); + + assert(scope_->kind() == Scope::GLOBAL); + + if (stmt->token() == TOK_NATIVE) { + // Define the native now. + size_t index; + if (!zone_->nativeTable()->add(stmt->name(), &index)) + return; + + Local native(zone_, Native::New(zone_, type, index)); + if (!native) + return; + + __ object(native); + __ setglobal(sym); + __ pop(); + } + + // If the function is a forward or native, we don't have to do anything. + if (stmt->token() == TOK_NATIVE || stmt->token() == TOK_FORWARD) + return; + + SemanticAnalysis child(this, type); + Local funobj(zone_, child.compile(stmt)); + if (!funobj) + return; + + __ object(funobj); + __ setglobal(sym); + if (stmt->token() == TOK_PUBLIC) + __ public_(); + else + __ pop(); +} + +Function * +SemanticAnalysis::compile(FunctionStatement *stmt) +{ + __ setFirstLine(stmt->pos().line); + + AutoEnterScope argScope(&scope_, stmt->argScope()); + + for (size_t i = 0; i < stmt->signature().parameters()->length(); i++) { + Parameter *param = stmt->signature().parameters()->at(i); + param->sym()->allocate(VariableSymbol::Arg, i); + } + + AutoEnterScope varScope(&scope_, stmt->varScope()); + ControlStatus status = visitForControl(stmt->body()); + + if (!fun_->returnType()->isVoid()) { + // If the function is not void, report if not all paths return a value. + // All other incorrect return cases have been caught already. + if (status.returns() == Returns_Sometimes) { + cc_.reportError(stmt->pos(), Message_NotAllPathsReturnValue); + return NULL; + } + + if (status.returns() == Returns_Never) { + cc_.reportError(stmt->pos(), Message_MissingReturnValue); + return NULL; + } + } + + Local code(zone_, __ finish()); + if (!code) + return NULL; + code->setModule(unit_->module()); + + Local fun(zone_, Function::New(zone_, code, fun_, stmt->name(), stmt->pos())); + if (!fun) + return NULL; + +#ifndef NDEBUG +// Disassemble(code, stdout); +#endif + + return fun; +} + +void +SemanticAnalysis::visitBlockStatement(BlockStatement *node) +{ + AutoEnterScope scope(&scope_, node->scope()); + + for (size_t i = 0; i < node->statements()->length(); i++) { + Statement *stmt = node->statements()->at(i); + __ note_position(stmt->pos()); + stmt->accept(this); + assert(__ stackDepth() == 0); + } +} + +void +SemanticAnalysis::visitEnumStatement(EnumStatement *node) +{ +} + +void +SemanticAnalysis::visitExpressionStatement(ExpressionStatement *node) +{ + // While building HIR, we enter new pool and root scopes, since HIR + // creation is purely temporary. + PoolScope poolScope(zone_->pool()); + RootScope rootScope(zone_); + + HIR *hir = eval(node->expression()); + if (!hir) + return; + + discharge(hir); + if (!hir->type()->isVoid()) + __ pop(); +} + +void +SemanticAnalysis::ControlScope::merge(const ControlStatus &left, const ControlStatus &right) +{ + // If both sides terminate in a return, we consider this a return as well. + if (left.returns() == Returns_Always && right.returns() == Returns_Always) { + terminate(Returns_Always); + return; + } + + ReturnStatus status = (left.returns() == Returns_Never && right.returns() == Returns_Never) + ? Returns_Never + : Returns_Sometimes; + + if (left.terminates() && right.terminates()) + terminate(status); + else + returns(status); +} + +SemanticAnalysis::ControlStatus +SemanticAnalysis::visitForControl(Statement *statement) +{ + ControlScope scope(&control_); + statement->accept(this); + return scope.status(); +} + +void +SemanticAnalysis::visitIfStatement(IfStatement *node) +{ + Label join; + + ControlStatus trueBranch; + ControlStatus falseBranch(false, Returns_Never); + + if (!node->ifFalse()) { + Label success; + + visitForTest(node->condition(), &success, &join, &success); + __ bind(&success); + trueBranch = visitForControl(node->ifTrue()); + } else { + Label success, next; + + visitForTest(node->condition(), &success, &next, &success); + __ bind(&success); + trueBranch = visitForControl(node->ifTrue()); + __ jump(&join); + __ bind(&next); + falseBranch = visitForControl(node->ifFalse()); + } + + __ bind(&join); + + control_->merge(trueBranch, falseBranch); +} + +void +SemanticAnalysis::requireLifoStack() +{ + if (!loop_) + return; + + loop_->setUsesLifoStack(); +} + +SemanticAnalysis::ControlStatus +SemanticAnalysis::visitLoopBody(Statement *stmt) +{ + // We always mark off a loop body, so on each edge, we can balance the + // LIFO stack. Right now, we don't have the ability to budget the LIFO + // stack ahead of time. If we turn out to never need the LIFO stack, we + // mark it as unnecessary. (The interpreter can't really make use of this, + // but the JIT can). + // + // Note that balancing the LIFO stack isn't even necessary, so this + // feature may be removed. All this mechanism does is mitigate the + // possibility that a long-running loop will overflow the LIFO region + // and therefore start requiring nursery allocations. + int pos = __ enterloop(); + ControlStatus status = visitForControl(stmt); + __ leaveloop(); + + if (loop_->usesLifoStack()) + __ setLoopUsesLifoStack(pos); + + return status; +} + +void +SemanticAnalysis::visitForStatement(ForStatement *node) +{ + AutoEnterScope scope(&scope_, node->scope()); + PoolScope poolScope(zone_->pool()); + RootScope rootScope(zone_); + + Label join, update; + LoopScope loop(scope_, &join, &update, &loop_); + + if (node->initialization()) + node->initialization()->accept(this); + + Label header, test; + if (node->condition()) + __ jump(&test); + + __ bind(&header); + ControlStatus status = visitLoopBody(node->body()); + + __ bind(&update); + if (node->update()) + node->update()->accept(this); + + if (node->condition()) { + __ bind(&test); + visitForTest(node->condition(), &header, &join, &join); + } else { + __ jump(&header); + } + + __ bind(&join); + + // If there was a condition, we treat this like a while loop. Otherwise, + // the join point is only reachable if the loop contains a break. The + // termination status doesn't tell us anything yet. + if (node->condition() || loop.breaks()) + control_->merge(status, ControlStatus(false, Returns_Never)); + else + control_->terminate(status.returns()); + + assert(__ stackDepth() == 0); +} + +void +SemanticAnalysis::visitWhileStatement(WhileStatement *node) +{ + PoolScope poolScope(zone_->pool()); + RootScope rootScope(zone_); + + Label header, join; + + __ bind(&header); + + ControlStatus status; + + LoopScope loop(scope_, &join, &header, &loop_); + if (node->token() == TOK_WHILE) { + Label body; + visitForTest(node->condition(), &body, &join, &body); + status = visitLoopBody(node->body()); + __ jump(&header); + } else { + status = visitLoopBody(node->body()); + visitForTest(node->condition(), &header, &join, &join); + } + + __ bind(&join); + + // Detect loops that will always have a fork in the contro-flow graph. + // This will be a |while| loop, or a |do-while| loop whose body does + // not terminate. For now, we treat these as an |if| regardless of the + // direction the branch can take. That means the control-flow is always + // conditional. + if (node->token() == TOK_WHILE || status.terminates() || loop.breaks()) { + control_->merge(status, ControlStatus(false, Returns_Never)); + } else { + // At this point, we've got a do-while loop with a |return| or + // |continue|, meaning that the rest of the block is unreachable. + control_->terminate(status.returns()); + } + + assert(__ stackDepth() == 0); +} + +static inline Heap::Tenure +InferTenure(Symbol *sym) +{ + if (sym->scope()->kind() == Scope::GLOBAL) + return Heap::Tenure_Old; + + // SP2's array semantics allow any array kind to escape, so we must use + // the normal heap. (Hopefully escape analysis can ease this pain later). + if (sym->type()->isArray()) + return Heap::Tenure_Default; + + return Heap::Tenure_Default; +} + +void +SemanticAnalysis::visitVariableDeclaration(VariableDeclaration *node) +{ + PoolScope poolScope(zone_->pool()); + RootScope rootScope(zone_); + + Local type(zone_, node->sym()->type()); + + if (node->sym()->type()->isVoid()) { + cc_.reportError(node->type()->pos(), Message_CannotDeclareVoid); + return; + } + + if (node->initialization()) { + HIR *hir = NULL; + + if ((hir = eval(node->initialization())) == NULL) + return; + if ((hir = coerce(hir, type, Coerce_Decl)) == NULL) + return; + + discharge(hir); + } else { + if (type->isInt32() || type->isEnum() || + type->isChar() || type->isBool()) + { + __ int_(0); + } else if (type->isFloat()) { + __ float_(0); + } else if (type->isArray()) { + Local atype(zone_, ArrayType::cast(type)); + Local map(zone_, atype->newMap()); + if (!map) + return; + + if (atype->isFixedLength()) { + __ newarray(OP_NEWFIXED, map, InferTenure(node->sym())); + } else if (node->dims()->at(0)) { + // This may not be true if we allow typedefing, so :TODO: + // figure out proper semantics. + assert(node->dims()->length() == atype->levels()); + + Local i32(zone_, zone_->types()->getPrimitive(PrimitiveType_Int32)); + for (size_t i = 0; i < node->dims()->length(); i++) { + Expression *expr = node->dims()->at(i); + if (expr) { + HIR *hir = eval(expr); + if (!hir || ((hir = coerce(hir, i32, Coerce_Arg)) == NULL)) + return; + discharge(hir); + } else { + __ int_(0); + } + } + __ newarray(OP_NEWSIZED, map, InferTenure(node->sym())); + } else { + __ newarray(OP_NEWEMPTY, map, InferTenure(node->sym())); + } + } else { + assert(false); + } + } + + if (node->sym()->scope()->kind() != Scope::GLOBAL) { + if (!__ allocate(node->sym())) + return; + } + + set(node->sym()); + __ pop(); +} + +void +SemanticAnalysis::visitReturnStatement(ReturnStatement *node) +{ + control_->returns(Returns_Always); + + if (!node->expression()) { + if (!fun_->returnType()->isVoid()) { + cc_.reportError(node->pos(), Message_MissingReturnValue); + return; + } + + __ returnvoid(); + return; + } + + if (fun_->returnType()->isVoid()) { + cc_.reportError(node->pos(), Message_UsedVoidReturn); + return; + } + + PoolScope poolScope(zone_->pool()); + RootScope rootScope(zone_); + + HIR *hir = eval(node->expression()); + if (!hir) + return; + + Local returnType(zone_, fun_->returnType()); + if ((hir = coerce(hir, returnType, Coerce_Return)) == NULL) + return; + + discharge(hir); + + if (hir->type()->isVoid()) + __ returnvoid(); + else + __ return_(); +} + +void +SemanticAnalysis::visitBinaryExpression(BinaryExpression *node) +{ + HIR *left = eval(node->left()); + HIR *right = eval(node->right()); + if (!left || !right) + return; + + hir_ = binary(node, node->token(), left, right); +} + +bool +SemanticAnalysis::checkArgumentCount(Handle type, unsigned actual) +{ + if (type->parameters()->length() == actual) + return true; + + if (actual > type->parameters()->length()) { + if (!type->isNative() || !type->isNativeVariadic()) + return false; + return true; + } + + if (!type->defaults()) + return false; + + // Missing arguments. See if there's a default argument for each missing + // actual. + for (unsigned i = actual; i < type->parameters()->length(); i++) { + if (!type->defaults()->at(i)) + return false; + } + + return true; +} + +static unsigned +BindImplicitName(Module *from, ImplicitSymbol *sym) +{ + if (sym->bound()) + return sym->fieldIndex(); + + Module *module = Module::cast(from->getImportAt(sym->index())); + StructMap *map = module->globals()->map(); + + unsigned fieldIndex; + Descriptor *desc = map->type()->lookupField(sym->name(), &fieldIndex); + assert(desc && desc->type() == sym->type()); + + sym->bind(fieldIndex); + return fieldIndex; +} + +void +SemanticAnalysis::visitNameProxy(NameProxy *proxy) +{ + Symbol *sym = proxy->sym(); + + switch (sym->kind()) { + case Symbol::kImport: + { + ImportSymbol *import = sym->toImport(); + hir_ = new (pool_) HImport(proxy, sym->type(), import->index()); + break; + } + + case Symbol::kImplicit: + { + ImplicitSymbol *import = sym->toImplicit(); + unsigned fieldIndex = BindImplicitName(unit_->module(), import); + + hir_ = new (pool_) HImport(proxy, zone_->types()->getImportable(), import->index()); + hir_ = new (pool_) HField(proxy, hir_, sym->type(), fieldIndex); + break; + } + + case Symbol::kConstant: + { + ConstantSymbol *val = sym->toConstant(); + hir_ = new (pool_) HInteger(proxy, sym->type(), val->value().toInt()); + break; + } + + // This is kind of screwy. Symbol::kFunction is like a constant we can't + // evaluate yet. Maybe we should extract the GLOBAL case and say that + // kFunction is only used for global functions (and use const vars for + // local functions). + case Symbol::kFunction: + case Symbol::kVariable: + { + Scope *in = sym->scope(); + switch (in->kind()) { + case Scope::GLOBAL: + hir_ = new (pool_) HGlobal(proxy, sym); + break; + + case Scope::FUNCTION: + { + VariableSymbol *var = sym->toVariable(); + assert(var->storage() == VariableSymbol::Arg); + hir_ = new (pool_) HLocal(proxy, var); + break; + } + + default: + { + VariableSymbol *var = sym->toVariable(); + assert(in->kind() == Scope::BLOCK); + assert(var->storage() == VariableSymbol::Local); + hir_ = new (pool_) HLocal(proxy, var); + break; + } + } + break; + } + + default: + // :TODO: + assert(false); + } +} + +void +SemanticAnalysis::visitBooleanLiteral(BooleanLiteral *node) +{ + Local type(zone_, zone_->types()->getPrimitive(PrimitiveType_Bool)); + hir_ = new (pool_) HBoolean(node, type, node->token() == TOK_TRUE); +} + +void +SemanticAnalysis::visitIntegerLiteral(IntegerLiteral *lit) +{ + Local type(zone_, zone_->types()->getPrimitive(PrimitiveType_Int32)); + hir_ = new (pool_) HInteger(lit, type, lit->value()); +} + +void +SemanticAnalysis::visitFloatLiteral(FloatLiteral *node) +{ + Local type(zone_, zone_->types()->getPrimitive(PrimitiveType_Float)); + hir_ = new (pool_) HFloat(node, type, (float)node->value()); +} + +void +SemanticAnalysis::visitStringLiteral(StringLiteral *node) +{ + Type *type = zone_->types()->getString(); + hir_ = new (pool_) HString(node, type, node->string()); +} + +void +SemanticAnalysis::visitCallExpression(CallExpression *node) +{ + HIR *callee = eval(node->callee()); + if (!callee) + return; + + if (!callee->type()->isFunction()) { + cc_.reportError(node->pos(), Message_CalleeNotFunction); + return; + } + + Local fun(zone_, FunctionType::cast(callee->type())); + + if (!checkArgumentCount(fun, node->arguments()->length())) { + cc_.reportError(node->pos(), Message_ArgumentCountMismatch); + return; + } + + if (fun->isForward()) { + cc_.reportError(node->pos(), Message_ForwardNotImplemented, fun->name()->chars()); + return; + } + + HIRList *args = new (pool_) HIRList; + for (unsigned i = 0; i < node->arguments()->length(); i++) { + Expression *expression = node->arguments()->at(i); + HIR *arg = eval(expression); + if (!arg) + return; + + Handle given = arg->type(); + + bool mustComputeReference; + if (i >= fun->parameters()->length()) { + // Special handling for natives. Arguments past the formal must + // always have references computed. However, arrays are already + // references for natives. + assert(fun->isNative() && fun->isNativeVariadic()); + mustComputeReference = !given->isReference() && + !given->isArray(); + } else { + Local actual(zone_, fun->parameterAt(i)); + + if (expression->isIndexExpression() && arg->isIndex()) { + // If the expression is the form x[y], and this produced a + // normal array indexing operation, then we apply some + // special rules. + + if (actual->isReference()) { + // Disabled, since this doesn't have easily definable or + // sensible semantics with SP2 arrays, but we don't want + // to silently break SP1 semantics. + cc_.reportError(expression->pos(), Message_CannotComputeIndexRef); + return; + } + + // As a special rule to account for SP1 semantics, we allow x[y] + // to automatically compute a sliced, read-only array view if + // the desired type is an array of one more dimension than x[y] + // would return. + // + // As an additional restriction, this is only allowed for + // one-level char arrays, to minimize potential issues until + // we determine whether more compatibility is needed. + // + // Internally, for sanity, the dependent array operation does + // not necessarily create a dependent array. For extensible + // arrays, we instead create a duplicate arrary of just the + // sliced portion, rather than deal with the complexity of the + // source array being resized. + // + // Note that we don't really know whether a dependent array + // will be dependent at compile-time, so the dependent type's + // just inherits from the source. + if (actual->isArray()) { + HIndex *index = arg->toIndex(); + Local basetype(zone_, ArrayType::cast(index->base()->type())); + Local atype(zone_, ArrayType::cast(actual)); + + if (atype->isCharArray() && basetype->isCharArray()) { + // There is no possible way this will type check, so + // it's safe to just try something completely + // different. + Local slicetype(zone_); + if (basetype->isFixedLength()) { + // If the base type is fixed length, we need to + // compute a new dynamic array type based on the + // fixed array's contents and qualifiers. + Local contained(zone_, basetype->contained()); + slicetype = zone_->types()->newArray(contained, ArrayType::DYNAMIC_ARRAY_SIZE); + if (!slicetype) + return; + slicetype = zone_->types()->qualify(slicetype, basetype->quals()); + if (!slicetype) + return; + } else { + slicetype = basetype; + } + arg = new (pool_) HNewDependentArray(expression, slicetype, index->base(), index->index()); + given = arg->type(); + } + } + } + + if (actual->isReference()) { + // If the actual is a reference, the coercion rules are much + // stricter; the contained types must be identical. + // :TODO: make this not an assert + assert(ReferenceType::cast(actual)->contained() == arg->type()); + } else { + // When we start overloading, coercion will have to happen later. + arg = coerce(arg, actual, Coerce_Arg); + if (!arg) + return; + } + + mustComputeReference = actual->isReference() && !given->isReference(); + } + + if (mustComputeReference) { + // Refs always use the LIFO stack if they can. + arg = new (pool_) HToRef(arg->node(), arg->type(), arg); + requireLifoStack(); + } + + if (!args->append(arg)) + return; + } + + hir_ = new (pool_) HCall(node, fun->returnType(), callee, args); +} + +void +SemanticAnalysis::visitAssignment(Assignment *node) +{ + HLValue *lval = lvalue(node->lvalue()); + if (!lval) + return; + + if (lval->type()->quals() & TypeQual_Const) { + cc_.reportError(node->pos(), Message_CannotModifyConst); + return; + } + + HIR *hir = eval(node->expression()); + if (!hir) + return; + if ((hir = coerce(hir, lval->type(), Coerce_Assign)) == NULL) + return; + + hir_ = new (pool_) HStore(node, node->token(), lval, hir); +} + +void +SemanticAnalysis::visitBreakStatement(BreakStatement *node) +{ + if (!loop_) { + cc_.reportError(node->pos(), Message_BreakOutsideLoop); + return; + } + + loop_->setBreaks(); + __ j(OP_BREAK, loop_->break_()); +} + +void +SemanticAnalysis::visitContinueStatement(ContinueStatement *node) +{ + if (!loop_) { + cc_.reportError(node->pos(), Message_ContinueOutsideLoop); + return; + } + + __ j(OP_CONTINUE, loop_->continue_()); +} + +void +SemanticAnalysis::visitIndexExpression(IndexExpression *node) +{ + HIR *left = eval(node->left()); + if (!left) + return; + + HIR *right = eval(node->right()); + if (!right) + return; + + // Hrm... yes, for now, let's coerce here. + Local i32(zone_, zone_->types()->getPrimitive(PrimitiveType_Int32)); + if ((right = coerce(right, i32, Coerce_Arg)) == NULL) + return; + + // Do not coerce here, there's no reason to yet. + if (!left->type()->isArray()) { + cc_.reportError(node->left()->pos(), Message_IndexBaseMustBeArray); + return; + } + + hir_ = new (pool_) HIndex(node, + ArrayType::cast(left->type())->contained(), + left, + right); +} + +void +SemanticAnalysis::visitIncDecExpression(IncDecExpression *node) +{ + HLValue *lval = lvalue(node->expression()); + if (!lval) + return; + + if (!lval->type()->isInt32() && !lval->type()->isFloat()) { + assert(false); + return; + } + + HIR *rval; + if (lval->type()->isInt32()) + rval = new (pool_) HInteger(node, lval->type(), 1); + else + rval = new (pool_) HFloat(node, lval->type(), 1.0); + + if (node->postfix()) + hir_ = new (pool_) HPostIncDec(node, node->token(), lval, rval); + else + hir_ = new (pool_) HStore(node, node->token(), lval, rval); +} + +Type * +SemanticAnalysis::coercionType(TokenKind token, HIR *left, HIR *right) +{ + Local l(zone_, left->type()); + Local r(zone_, right->type()); + + if (l->isPod()) + l = l->unqualified(); + if (r->isPod()) + r = r->unqualified(); + + if ((token >= TOK_PLUS && token <= TOK_SLASH) || + (token >= TOK_EQUALS && token <= TOK_GE)) + { + // We allow: [float|int32] + [float|int32] + if (l->isFloat() && r->isInt32()) + return l; + if (l->isInt32() && r->isFloat()) + return r; + if (l->isInt32() && r->isInt32()) + return l; + + if (l->isChar() && r->isInt32()) + return r; + if (l->isInt32() && r->isChar()) + return l; + } + + // :TODO: remove + assert(*l == *r); + + return left->type(); +} + +HIR * +SemanticAnalysis::typeCheckArray(const SourcePosition &pos, + HIR *from, + Handle argActual, + CoercionKind kind) +{ + Local argGiven(zone_, from->type()); + + // Special case: if we receive a string literal, and this is a char array, + // we allow a coercion. + if (from->isString() && + argGiven->isString() && + argActual->isArray() && + ArrayType::cast(argActual)->isCharArray()) + { + HString *hir = from->toString(); + Local actual(zone_, ArrayType::cast(argActual)); + + if (actual->isFixedLength()) { + if (hir->string()->length() + 1 > actual->fixedLength()) { + cc_.reportError(pos, Message_ArrayAssignmentTooBig); + return NULL; + } + } + + // Transform the string literal into an array literal. We give it the + // type of the left-hand side. The constness of the destination type + // will determine whether HArray duplicates it or not. + Local map(zone_, actual->newMap()); + if (!map) + return NULL; + + Local array(zone_); + array = Array::NewString(zone_, + map, + hir->string()->chars(), + hir->string()->length(), + Heap::Tenure_Old); + if (!array) + return NULL; + + // Since we're creating a canonical copy of the char array, we have to + // be careful that it doesn't get mutated. There are two cases where + // we know it can't be mutated, and therefore don't need to specify + // that it be cloned after pushing it onto the stack. + // + // (1) The array type is const. + // (2) The array type is fixed-length, and the coercion is for + // assignment, meaning it will be immediately copied. + // + // We treat declaration separately from assignment, since for + // declaration we can just immediately use the cloned version. + bool shouldDuplicate = true; + if ((actual->quals() & TypeQual_Const) || + (actual->isFixedLength() && kind == Coerce_Assign)) + { + shouldDuplicate = false; + } + + return new (pool_) HArray(hir->node(), actual, array, shouldDuplicate); + } + + if (!argGiven->isArray()) { + cc_.reportError(pos, Message_ExpectedArray); + return NULL; + } + + Local actual(zone_, ArrayType::cast(argActual)); + Local given(zone_, ArrayType::cast(argGiven)); + + if (actual->levels() != given->levels()) { + cc_.reportError(pos, Message_ArrayDimensionsMismatch); + return NULL; + } + + unsigned level = 1; + while (true) { + if (actual->isFixedLength()) { + if (!given->isFixedLength()) { + cc_.reportError(pos, Message_ArrayDimensionSizeMismatch, level); + return NULL; + } + + if (actual->isCharArray()) { + // String assignment can have a truncated |given|. + if (given->fixedLength() > actual->fixedLength()) { + cc_.reportError(pos, Message_ArrayAssignmentTooBig); + return NULL; + } + } else { + if (given->fixedLength() != actual->fixedLength()) { + cc_.reportError(pos, Message_ArrayDimensionSizeMismatch, level); + return NULL; + } + } + } else if (given->isFixedLength()) { + // We do not allow assignments in the form: + // [][] = [][Y] + // + // Otherwise, the following code could break the size guarantees + // of the right-hand side: + // + // new x[][5]; + // new y[][]; + // new z[3]; + // + // + // y = x; + // y[0] = z; + // + // Now |x[0]| contains a 3-length array. + if (level > 1) { + cc_.reportError(pos, Message_ArrayDimensionSizeMismatch, level); + return NULL; + } + } + + if (!given->contained()->isArray() || !actual->contained()->isArray()) + break; + + given = ArrayType::cast(given->contained()); + actual = ArrayType::cast(actual->contained()); + } + + bool givenConst = !!(given->contained()->quals() & TypeQual_Const); + bool actualConst = !!(actual->contained()->quals() & TypeQual_Const); + + // These arrays must contain the same innermost type. + if (given->contained()->unqualified() != actual->contained()->unqualified() || + (givenConst && !actualConst)) + { + char aname[255]; + char gname[255]; + cc_.reportError(pos, Message_TypeMismatch, + GetTypeName(argActual, aname, sizeof(aname)), + GetTypeName(argGiven, gname, sizeof(gname))); + return NULL; + } + + return from; +} + +HIR * +SemanticAnalysis::typeFailure(const SourcePosition &pos, Handle from, Handle to) +{ + char fname[255]; + char tname[255]; + cc_.reportError(pos, Message_InvalidCoercion, + GetTypeName(from, fname, sizeof(fname)), + GetTypeName(to, tname, sizeof(tname))); + return NULL; +} + +HIR * +SemanticAnalysis::coerce(HIR *hir, Handle to, CoercionKind kind) +{ + Local from(zone_, hir->type()); + + // If the source is *ever* an importable, we just fail. + if (from->isImportable()) + return typeFailure(hir->node()->pos(), from, to); + + if (from->isInt32() && to->isFloat()) + return new (pool_) HConvert(hir->node(), to, OP_CVT_I2F, hir); + if (from->isInt32() && to->isBool()) + return new (pool_) HConvert(hir->node(), to, OP_CVT_I2B, hir); + + if (to->isEnum() && from->unqualified() != to->unqualified()) + return typeFailure(hir->node()->pos(), from, to); + + if (to->isArray()) { + Local atype(zone_, ArrayType::cast(to)); + return typeCheckArray(hir->node()->pos(), hir, atype, kind); + } + + return hir; +} + +void +SemanticAnalysis::set(VariableSymbol *sym) +{ + if (sym->scope()->kind() == Scope::GLOBAL) { + __ setglobal(sym); + } else { + assert(sym->storage() == VariableSymbol::Local); + __ setlocal(sym); + } +} + +HIR * +SemanticAnalysis::binary(AstNode *node, TokenKind token, HIR *left, HIR *right) +{ + Local coercion(zone_, coercionType(token, left, right)); + if (!coercion) + return NULL; + if ((left = coerce(left, coercion, Coerce_Arg)) == NULL) + return NULL; + if ((right = coerce(right, coercion, Coerce_Arg)) == NULL) + return NULL; + + switch (token) { + case TOK_PLUS: + case TOK_MINUS: + case TOK_STAR: + case TOK_SLASH: + case TOK_PERCENT: + case TOK_AMPERSAND: + case TOK_BITOR: + case TOK_BITXOR: + case TOK_SHR: + case TOK_USHR: + case TOK_SHL: + return new (pool_) HBinary(node, left->type(), token, left, right); + + case TOK_EQUALS: + case TOK_NOTEQUALS: + case TOK_LT: + case TOK_LE: + case TOK_GT: + case TOK_GE: + case TOK_AND: + case TOK_OR: + { + Local result(zone_, zone_->types()->getPrimitive(PrimitiveType_Bool)); + return new (pool_) HBinary(node, result, token, left, right); + } + + default: + assert(false); + return NULL; + } +} + +static inline bool +IsValidUncheckedType(Type *type) +{ + return type->isEnum() || + type->isFloat() || + type->isInt32() || + type->isChar() || + type->isUnchecked() || + type->isBool(); +} + +void +SemanticAnalysis::visitUnaryExpression(UnaryExpression *node) +{ + HIR *hir = eval(node->expression()); + if (!hir) + return; + + switch (node->token()) { + case TOK_TILDE: + { + if (!hir->type()->isInt32() && !hir->type()->isEnum()) { + char name[255]; + cc_.reportError(node->pos(), Message_InvalidUnaryType, + "~", + GetTypeName(hir->type(), name, sizeof(name))); + } + Local type(zone_, zone_->types()->getPrimitive(PrimitiveType_Int32)); + hir_ = new (pool_) HUnary(node, hir->type(), TOK_TILDE, hir); + return; + } + + case TOK_MINUS: + { + if (hir->type()->isInt32() || hir->type()->isEnum()) { + Local type(zone_, zone_->types()->getPrimitive(PrimitiveType_Int32)); + hir_ = new (pool_) HUnary(node, type, TOK_MINUS, hir); + return; + } + if (hir->type()->isFloat()) { + hir_ = new (pool_) HUnary(node, hir->type(), TOK_MINUS, hir); + return; + } + + char name[255]; + cc_.reportError(node->pos(), Message_InvalidUnaryType, + "-", + GetTypeName(hir->type(), name, sizeof(name))); + break; + } + + default: + { + assert(node->token() == TOK_LABEL); + + TypeSymbol *sym = node->tag()->sym()->asType(); + if (!sym) { + cc_.reportError(node->pos(), Message_IdentifierIsNotAType, + sym->name()->chars()); + return; + } + + if (!IsValidUncheckedType(sym->effective())) { + char name[255]; + cc_.reportError(node->pos(), Message_InvalidUncheckedCastTo, + GetTypeName(sym->type(), name, sizeof(name))); + return; + } + if (!IsValidUncheckedType(hir->type())) { + char name[255]; + cc_.reportError(node->pos(), Message_InvalidUncheckedCastFrom, + GetTypeName(hir->type(), name, sizeof(name))); + return; + } + + hir_ = new (pool_) HUnary(node, sym->effective(), TOK_LABEL, hir); + break; + } + } +} + +void +SemanticAnalysis::import(HIR *hir, FieldExpression *node) +{ + // Note that we don't care if the hir type is "importable", since we + // can only do anything if the important has a constant binding. + unsigned index = hir->toImport()->index(); + Local importable(zone_, unit_->module()->getImportAt(index)); + + if (importable->isPackage()) { + // Look at what's already loaded and see if anything exists. We + // can't create stuff on-demand here since it's too late to add + // new translation units to the CompileContext. + Local package(zone_, Package::cast(importable)); + Local found(zone_, package->import(node->field())); + if (!found) { + cc_.reportError(node->pos(), Message_PackageDoesNotHaveMember, node->field()->chars()); + return; + } + + // If we did find something, make sure it is in our import list. + // If we didn't explicitly import it, we have no right to use it. + for (index = 0; index < unit_->module()->imports(); index++) { + if (unit_->module()->getImportAt(index) == importable) + break; + } + + if (index == unit_->module()->imports()) { + cc_.reportError(node->pos(), Message_ImportWasNeverImported, node->field()->chars()); + return; + } + + // Just create a new HImport. + hir_ = new (pool_) HImport(node, hir->type(), index); + } else { + // Modules can never have sub-modules or sub-packages. + unsigned index; + Local module(zone_, Module::cast(importable)); + Local map(zone_, module->globals()->map()); + Local desc(zone_, map->type()->lookupField(node->field(), &index)); + if (!desc) { + cc_.reportError(node->pos(), Message_FieldNotFound, node->field()->chars()); + return; + } + hir_ = new (pool_) HField(node, hir, desc->type(), index); + } +} + +void +SemanticAnalysis::visitFieldExpression(FieldExpression *node) +{ + HIR *hir = eval(node->left()); + if (!hir) + return; + + if (hir->isImport()) { + import(hir, node); + return; + } + + char name[255]; + cc_.reportError(node->pos(), Message_InvalidFieldExpression, + GetTypeName(hir->type(), name, sizeof(name))); +} + +static inline TokenKind +ToCompare(Expression *expr) +{ + if (expr->isBinaryExpression()) { + BinaryExpression *b = expr->toBinaryExpression(); + if (b->token() >= TOK_EQUALS && b->token() <= TOK_OR) + return b->token(); + } else if (expr->isUnaryExpression()) { + UnaryExpression *u = expr->toUnaryExpression(); + if (u->token() == TOK_NOT) + return TOK_NOT; + } + return TOK_EOF; +} + +void +SemanticAnalysis::visitForTest(Expression *expr, + Label *trueBranch, + Label *falseBranch, + Label *fallthrough) +{ + Local boolType(zone_, zone_->types()->getPrimitive(PrimitiveType_Bool)); + + TokenKind token = ToCompare(expr); + if (token == TOK_EOF || token == TOK_NOT) { + HIR *hir; + if (token == TOK_EOF) + hir = eval(expr); + else + hir = eval(expr->toUnaryExpression()->expression()); + if (!hir) + return; + if ((hir = coerce(hir, boolType, Coerce_Arg)) == NULL) + return; + + discharge(hir); + + bool jumpOnTrue = (fallthrough == falseBranch) ^ (token == TOK_NOT); + Opcode op = jumpOnTrue ? OP_JT : OP_JF; + + if (fallthrough == falseBranch) + __ j(op, trueBranch); + else + __ j(op, falseBranch); + return; + } + + BinaryExpression *bin = expr->toBinaryExpression(); + if (token == TOK_AND || token == TOK_OR) { + Label next; + if (token == TOK_AND) + visitForTest(bin->left(), &next, falseBranch, &next); + else + visitForTest(bin->left(), trueBranch, &next, &next); + __ bind(&next); + visitForTest(bin->right(), trueBranch, falseBranch, fallthrough); + return; + } + + HIR *left = eval(bin->left()); + HIR *right = eval(bin->right()); + if (!left || !right) + return; + + HIR *hir = binary(bin, bin->token(), left, right); + if (!hir) + return; + if ((hir = coerce(hir, boolType, Coerce_Arg)) == NULL) + return; + + discharge(hir); + if (fallthrough == trueBranch) + __ j(OP_JF, falseBranch); + else + __ j(OP_JT, trueBranch); +} + +HLValue * +SemanticAnalysis::lvalue(Expression *expr) +{ + HIR *hir = eval(expr); + if (!hir) + return NULL; + + if (!hir->isLValue()) { + cc_.reportError(expr->pos(), Message_ExpectedLValue); + return NULL; + } + + return static_cast(hir); +} + +HIR * +SemanticAnalysis::eval(Expression *expr) +{ + assert(!hir_); + expr->accept(this); + + HIR *hir = hir_; + hir_ = NULL; + + return hir; +} + +void +SemanticAnalysis::discharge(HIR *hir) +{ + EmitHIR(zone_, emitter_, hir); +} diff -r c4b1341297e5 -r ba85a47ee414 src/compiler/SemanticAnalysis.h --- a/src/compiler/SemanticAnalysis.h Mon Jun 10 00:46:29 2013 +0100 +++ b/src/compiler/SemanticAnalysis.h Mon Jun 10 01:04:43 2013 +0100 @@ -1,259 +1,259 @@ -/* - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#ifndef _include_sp2_sema_h_ -#define _include_sp2_sema_h_ - -#include "AST.h" -#include "HIR.h" -#include "Scopes.h" -#include "BytecodeEmitter.h" - -namespace ke { - -// Semantic analysis occurs after all names and types have been bound. It -// performs the important task of validating the semantics and type safety -// of a parse tree. -// -// The semantic analysis phase also produces bytecode. This occurs on a -// statement-by-statement basis. The result of analyzing an expression is a -// tree of operations which is then flattened. -class SemanticAnalysis : public AstVisitor -{ - public: - SemanticAnalysis(Zone *zone, CompileContext &cc, TranslationUnit *unit); - - void analyze(); - - private: - SemanticAnalysis(SemanticAnalysis *parent, Handle fun); - Function *compile(FunctionStatement *stmt); - - public: - void visitFunctionStatement(FunctionStatement *node); - void visitBlockStatement(BlockStatement *node); - void visitExpressionStatement(ExpressionStatement *node); - void visitReturnStatement(ReturnStatement *node); - void visitEnumStatement(EnumStatement *node); - - void visitCallExpression(CallExpression *node); - void visitNameProxy(NameProxy *name); - void visitIntegerLiteral(IntegerLiteral *node); - void visitFloatLiteral(FloatLiteral *node); - void visitVariableDeclaration(VariableDeclaration *node); - void visitForStatement(ForStatement *node); - void visitWhileStatement(WhileStatement *node); - void visitAssignment(Assignment *node); - void visitBinaryExpression(BinaryExpression *node); - void visitBreakStatement(BreakStatement *node); - void visitContinueStatement(ContinueStatement *node); - void visitIfStatement(IfStatement *node); - void visitIndexExpression(IndexExpression *node); - void visitIncDecExpression(IncDecExpression *node); - void visitStringLiteral(StringLiteral *node); - void visitUnaryExpression(UnaryExpression *node); - void visitBooleanLiteral(BooleanLiteral *node); - void visitFieldExpression(FieldExpression *node); -#if 0 - void visitFunctionTypeStatement(FunctionTypeStatement *node); - void visitImportStatement(ImportStatement *node); - void visitStructureStatement(StructureStatement *node); - void visitSwitchStatement(SwitchStatement *node); - - void visitTernaryExpression(TernaryExpression *node); - void visitArrayLiteral(ArrayLiteral *node); -#endif - - private: - bool checkArgumentCount(Handle type, unsigned actual); - void import(HIR *hir, FieldExpression *node); - - private: - enum CoercionKind { - Coerce_Arg, - Coerce_Return, - Coerce_Decl, - Coerce_Assign - }; - - // For a given control flow path, whether it returns. - enum ReturnStatus - { - Returns_Never, - Returns_Sometimes, - Returns_Always - }; - - // For a given control flow path, whether it terminates, - // and if it terminates, whether it returns. - class ControlStatus - { - bool terminates_; - ReturnStatus returns_; - - public: - ControlStatus() - { - } - ControlStatus(bool terminates, ReturnStatus returns) - : terminates_(terminates), - returns_(returns) - { - } - - bool terminates() const { - return terminates_; - } - ReturnStatus returns() const { - return returns_; - } - }; - - class ControlScope - { - ReturnStatus returns_; - ControlScope **prevp_; - ControlScope *prev_; - bool terminates_; - - public: - ControlScope(ControlScope **prevp) - : returns_(Returns_Never), - prevp_(prevp), - prev_(*prevp), - terminates_(false) - { - *prevp_ = this; - } - ~ControlScope() { - *prevp_ = prev_; - } - ControlStatus status() { - return ControlStatus(terminates_, returns_); - } - - void returns(ReturnStatus status) { - // We allow multiple terminates, but only the first one takes - // effect. For example, |break; continue;| is legal, but the - // continue is unreachable. - if (terminates_) - return; - if (status > returns_) - returns_ = status; - } - void terminate(ReturnStatus status) { - if (terminates_) - return; - terminates_ = true; - returns_ = status; - } - void merge(const ControlStatus &left, const ControlStatus &right); - void inherit(const ControlStatus &other); - }; - - - void requireLifoStack(); - ControlStatus visitForControl(Statement *statement); - ControlStatus visitLoopBody(Statement *stmt); - void visitForTest(Expression *expr, - BytecodeEmitter::Label *trueBranch, - BytecodeEmitter::Label *falseBranch, - BytecodeEmitter::Label *fallthrough); - void set(VariableSymbol *sym); - HIR *eval(Expression *expr); - HIR *binary(AstNode *node, TokenKind token, HIR *left, HIR *right); - HIR *coerce(HIR *from, Handle to, CoercionKind kind); - Type *coercionType(TokenKind token, HIR *left, HIR *right); - HLValue *lvalue(Expression *expr); - void discharge(HIR *hir); - HIR *typeFailure(const SourcePosition &pos, Handle from, Handle to); - - HIR *typeCheckArray(const SourcePosition &pos, - HIR *from, - Handle argActual, - CoercionKind kind); - - private: - // Used to target break/catch statements. - class LoopScope - { - Scope *scope_; - BytecodeEmitter::Label *bk_; - BytecodeEmitter::Label *ct_; - LoopScope **prevp_; - LoopScope *prev_; - bool usesLifoStack_; - bool breaks_; - - public: - LoopScope(Scope *scope, BytecodeEmitter::Label *bk, BytecodeEmitter::Label *ct, - LoopScope **prevp) - : scope_(scope), - bk_(bk), - ct_(ct), - prevp_(prevp), - prev_(*prevp), - usesLifoStack_(false), - breaks_(false) - { - *prevp_ = this; - } - - ~LoopScope() - { - *prevp_ = prev_; - } - - BytecodeEmitter::Label *break_() { - return bk_; - } - BytecodeEmitter::Label *continue_() { - return ct_; - } - Scope *scope() { - return scope_; - } - void setUsesLifoStack() { - usesLifoStack_ = true; - } - bool usesLifoStack() const { - return usesLifoStack_; - } - void setBreaks() { - breaks_ = true; - } - bool breaks() const { - return breaks_; - } - }; - - private: - Zone *zone_; - PoolAllocator &pool_; - CompileContext &cc_; - TranslationUnit *unit_; - HIR *hir_; - Scope *scope_; - BytecodeEmitter emitter_; - ScopedRoot fun_; - LoopScope *loop_; - ControlScope *control_; -}; - -} // namespace ke - -#endif // _include_sp2_sema_h_ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_sp2_sema_h_ +#define _include_sp2_sema_h_ + +#include "AST.h" +#include "HIR.h" +#include "Scopes.h" +#include "BytecodeEmitter.h" + +namespace ke { + +// Semantic analysis occurs after all names and types have been bound. It +// performs the important task of validating the semantics and type safety +// of a parse tree. +// +// The semantic analysis phase also produces bytecode. This occurs on a +// statement-by-statement basis. The result of analyzing an expression is a +// tree of operations which is then flattened. +class SemanticAnalysis : public AstVisitor +{ + public: + SemanticAnalysis(Zone *zone, CompileContext &cc, TranslationUnit *unit); + + void analyze(); + + private: + SemanticAnalysis(SemanticAnalysis *parent, Handle fun); + Function *compile(FunctionStatement *stmt); + + public: + void visitFunctionStatement(FunctionStatement *node); + void visitBlockStatement(BlockStatement *node); + void visitExpressionStatement(ExpressionStatement *node); + void visitReturnStatement(ReturnStatement *node); + void visitEnumStatement(EnumStatement *node); + + void visitCallExpression(CallExpression *node); + void visitNameProxy(NameProxy *name); + void visitIntegerLiteral(IntegerLiteral *node); + void visitFloatLiteral(FloatLiteral *node); + void visitVariableDeclaration(VariableDeclaration *node); + void visitForStatement(ForStatement *node); + void visitWhileStatement(WhileStatement *node); + void visitAssignment(Assignment *node); + void visitBinaryExpression(BinaryExpression *node); + void visitBreakStatement(BreakStatement *node); + void visitContinueStatement(ContinueStatement *node); + void visitIfStatement(IfStatement *node); + void visitIndexExpression(IndexExpression *node); + void visitIncDecExpression(IncDecExpression *node); + void visitStringLiteral(StringLiteral *node); + void visitUnaryExpression(UnaryExpression *node); + void visitBooleanLiteral(BooleanLiteral *node); + void visitFieldExpression(FieldExpression *node); +#if 0 + void visitFunctionTypeStatement(FunctionTypeStatement *node); + void visitImportStatement(ImportStatement *node); + void visitStructureStatement(StructureStatement *node); + void visitSwitchStatement(SwitchStatement *node); + + void visitTernaryExpression(TernaryExpression *node); + void visitArrayLiteral(ArrayLiteral *node); +#endif + + private: + bool checkArgumentCount(Handle type, unsigned actual); + void import(HIR *hir, FieldExpression *node); + + private: + enum CoercionKind { + Coerce_Arg, + Coerce_Return, + Coerce_Decl, + Coerce_Assign + }; + + // For a given control flow path, whether it returns. + enum ReturnStatus + { + Returns_Never, + Returns_Sometimes, + Returns_Always + }; + + // For a given control flow path, whether it terminates, + // and if it terminates, whether it returns. + class ControlStatus + { + bool terminates_; + ReturnStatus returns_; + + public: + ControlStatus() + { + } + ControlStatus(bool terminates, ReturnStatus returns) + : terminates_(terminates), + returns_(returns) + { + } + + bool terminates() const { + return terminates_; + } + ReturnStatus returns() const { + return returns_; + } + }; + + class ControlScope + { + ReturnStatus returns_; + ControlScope **prevp_; + ControlScope *prev_; + bool terminates_; + + public: + ControlScope(ControlScope **prevp) + : returns_(Returns_Never), + prevp_(prevp), + prev_(*prevp), + terminates_(false) + { + *prevp_ = this; + } + ~ControlScope() { + *prevp_ = prev_; + } + ControlStatus status() { + return ControlStatus(terminates_, returns_); + } + + void returns(ReturnStatus status) { + // We allow multiple terminates, but only the first one takes + // effect. For example, |break; continue;| is legal, but the + // continue is unreachable. + if (terminates_) + return; + if (status > returns_) + returns_ = status; + } + void terminate(ReturnStatus status) { + if (terminates_) + return; + terminates_ = true; + returns_ = status; + } + void merge(const ControlStatus &left, const ControlStatus &right); + void inherit(const ControlStatus &other); + }; + + + void requireLifoStack(); + ControlStatus visitForControl(Statement *statement); + ControlStatus visitLoopBody(Statement *stmt); + void visitForTest(Expression *expr, + BytecodeEmitter::Label *trueBranch, + BytecodeEmitter::Label *falseBranch, + BytecodeEmitter::Label *fallthrough); + void set(VariableSymbol *sym); + HIR *eval(Expression *expr); + HIR *binary(AstNode *node, TokenKind token, HIR *left, HIR *right); + HIR *coerce(HIR *from, Handle to, CoercionKind kind); + Type *coercionType(TokenKind token, HIR *left, HIR *right); + HLValue *lvalue(Expression *expr); + void discharge(HIR *hir); + HIR *typeFailure(const SourcePosition &pos, Handle from, Handle to); + + HIR *typeCheckArray(const SourcePosition &pos, + HIR *from, + Handle argActual, + CoercionKind kind); + + private: + // Used to target break/catch statements. + class LoopScope + { + Scope *scope_; + BytecodeEmitter::Label *bk_; + BytecodeEmitter::Label *ct_; + LoopScope **prevp_; + LoopScope *prev_; + bool usesLifoStack_; + bool breaks_; + + public: + LoopScope(Scope *scope, BytecodeEmitter::Label *bk, BytecodeEmitter::Label *ct, + LoopScope **prevp) + : scope_(scope), + bk_(bk), + ct_(ct), + prevp_(prevp), + prev_(*prevp), + usesLifoStack_(false), + breaks_(false) + { + *prevp_ = this; + } + + ~LoopScope() + { + *prevp_ = prev_; + } + + BytecodeEmitter::Label *break_() { + return bk_; + } + BytecodeEmitter::Label *continue_() { + return ct_; + } + Scope *scope() { + return scope_; + } + void setUsesLifoStack() { + usesLifoStack_ = true; + } + bool usesLifoStack() const { + return usesLifoStack_; + } + void setBreaks() { + breaks_ = true; + } + bool breaks() const { + return breaks_; + } + }; + + private: + Zone *zone_; + PoolAllocator &pool_; + CompileContext &cc_; + TranslationUnit *unit_; + HIR *hir_; + Scope *scope_; + BytecodeEmitter emitter_; + ScopedRoot fun_; + LoopScope *loop_; + ControlScope *control_; +}; + +} // namespace ke + +#endif // _include_sp2_sema_h_ diff -r c4b1341297e5 -r ba85a47ee414 src/compiler/Symbols.cpp --- a/src/compiler/Symbols.cpp Mon Jun 10 00:46:29 2013 +0100 +++ b/src/compiler/Symbols.cpp Mon Jun 10 01:04:43 2013 +0100 @@ -1,22 +1,22 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of JITCraft. - * - * JITCraft is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * JITCraft. If not, see http://www.gnu.org/licenses/. - */ -#include "Symbols.h" -#include "../Heap-inl.h" -#include "../Handles-inl.h" - +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include "Symbols.h" +#include "../Heap-inl.h" +#include "../Handles-inl.h" + diff -r c4b1341297e5 -r ba85a47ee414 src/compiler/Symbols.h --- a/src/compiler/Symbols.h Mon Jun 10 00:46:29 2013 +0100 +++ b/src/compiler/Symbols.h Mon Jun 10 01:04:43 2013 +0100 @@ -1,278 +1,278 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#ifndef _include_sp2_symbol_h_ -#define _include_sp2_symbol_h_ - -#include "../PoolAllocator.h" -#include "../Handles.h" -#include "../Opcodes.h" -#include "../Types.h" -#include "../Strings.h" - -namespace ke { - -class String; - -#define SYMBOL_KINDS(_) \ - /* Any kind of variable or argument produces a VariableSymbol. */ \ - _(Variable) \ - /* A function declaration produces a FunctionSymbol. */ \ - _(Function) \ - /* A named constant produces a ConstantSymbol. */ \ - _(Constant) \ - /* A named type (class struct, typedef, etc) produces a TypeSymbol. */ \ - _(Type) \ - /* An import name. */ \ - _(Import) \ - /* A name implicitly derived from an import. */ \ - _(Implicit) - -#define _(name) class name##Symbol; -SYMBOL_KINDS(_) -#undef _ - -class Scope; - -// A symbol represents the declaration of a named entity. -class Symbol : public PoolObject -{ - public: - enum Kind { -# define _(name) k##name, - SYMBOL_KINDS(_) -# undef _ - kTotalSymbolKinds - }; - - public: - Symbol(Scope *scope, Handle name, const SourcePosition &pos) - : scope_(scope), - name_(name), - pos_(pos) - { - } - - virtual Kind kind() const = 0; - - Handle name() const { - return name_; - } - const SourcePosition &pos() const { - return pos_; - } - Handle type() const { - return type_; - } - Scope *scope() const { - return scope_; - } - - public: -#define _(name) \ - bool is##name() const { \ - return (kind() == k##name); \ - } \ - name##Symbol *as##name() { \ - if (is##name()) \ - return to##name(); \ - return NULL; \ - } \ - name##Symbol *to##name() { \ - assert(is##name()); \ - return reinterpret_cast(this); \ - } - SYMBOL_KINDS(_) -#undef _ - - private: - Scope *scope_; - ScopedRoot name_; - SourcePosition pos_; - - protected: - ScopedRoot type_; -}; - -class VariableSymbol : public Symbol -{ - public: - enum Storage { - Unknown, - Local, - Arg, - Heap - }; - - public: - VariableSymbol(Scope *scope, Handle name, const SourcePosition &pos) - : Symbol(scope, name, pos), - storage_(Unknown) - { - } - - VariableSymbol(Scope *scope, Handle name, const SourcePosition &pos, Handle type) - : Symbol(scope, name, pos), - storage_(Unknown) - { - type_ = type; - } - - Kind kind() const { - return kVariable; - } - void setType(Type *type) { - assert(!type_); - type_ = type; - } - void allocate(Storage storage, unsigned slot) { - storage_ = storage; - slot_ = slot; - } - Storage storage() const { - return storage_; - } - unsigned slot() const { - assert(storage() != Unknown); - return slot_; - } - - private: - Storage storage_; - unsigned slot_; -}; - -class TypeSymbol : public Symbol -{ - public: - TypeSymbol(Scope *scope, Handle name, Handle type, Handle effective) - : Symbol(scope, name, SourcePosition()), - effective_(effective) - { - type_ = type; - } - - Kind kind() const { - return kType; - } - Handle effective() { - return effective_; - } - - private: - ScopedRoot effective_; -}; - -class FunctionSymbol : public Symbol -{ - public: - FunctionSymbol(Scope *scope, Handle name, const SourcePosition &pos, Handle type) - : Symbol(scope, name, pos) - { - type_ = type; - } - - Kind kind() const { - return kFunction; - } - FunctionType *type() const { - return FunctionType::cast(type_); - } -}; - -class ConstantSymbol : public Symbol -{ - public: - ConstantSymbol(Scope *scope, Handle name, const SourcePosition &pos, Handle type, - const BoxedPrimitive &prim) - : Symbol(scope, name, pos), - value_(prim) - { - type_ = type; - } - - Kind kind() const { - return kConstant; - } - const BoxedPrimitive &value() const { - return value_; - } - - private: - BoxedPrimitive value_; -}; - -class ImportSymbol : public Symbol -{ - public: - ImportSymbol(Scope *scope, Handle name, const SourcePosition &pos, Handle type, - unsigned index) - : Symbol(scope, name, pos), - index_(index) - { - type_ = type; - } - - Kind kind() const { - return kImport; - } - unsigned index() const { - return index_; - } - - private: - unsigned index_; -}; - -class ImplicitSymbol : public Symbol -{ - public: - ImplicitSymbol(Scope *scope, Handle name, const SourcePosition &pos, Handle type, - unsigned index) - : Symbol(scope, name, pos), - index_(index), - fieldIndex_(unsigned(-1)) - { - type_ = type; - } - - bool bound() const { - return fieldIndex_ != unsigned(-1); - } - Kind kind() const { - return kImplicit; - } - unsigned index() const { - return index_; - } - void bind(unsigned fieldIndex) { - fieldIndex_ = fieldIndex; - } - unsigned fieldIndex() const { - assert(bound()); - return fieldIndex_; - } - - private: - unsigned index_; - unsigned fieldIndex_; -}; - -} - -#endif // _include_sp2_symbol_h_ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_sp2_symbol_h_ +#define _include_sp2_symbol_h_ + +#include "../PoolAllocator.h" +#include "../Handles.h" +#include "../Opcodes.h" +#include "../Types.h" +#include "../Strings.h" + +namespace ke { + +class String; + +#define SYMBOL_KINDS(_) \ + /* Any kind of variable or argument produces a VariableSymbol. */ \ + _(Variable) \ + /* A function declaration produces a FunctionSymbol. */ \ + _(Function) \ + /* A named constant produces a ConstantSymbol. */ \ + _(Constant) \ + /* A named type (class struct, typedef, etc) produces a TypeSymbol. */ \ + _(Type) \ + /* An import name. */ \ + _(Import) \ + /* A name implicitly derived from an import. */ \ + _(Implicit) + +#define _(name) class name##Symbol; +SYMBOL_KINDS(_) +#undef _ + +class Scope; + +// A symbol represents the declaration of a named entity. +class Symbol : public PoolObject +{ + public: + enum Kind { +# define _(name) k##name, + SYMBOL_KINDS(_) +# undef _ + kTotalSymbolKinds + }; + + public: + Symbol(Scope *scope, Handle name, const SourcePosition &pos) + : scope_(scope), + name_(name), + pos_(pos) + { + } + + virtual Kind kind() const = 0; + + Handle name() const { + return name_; + } + const SourcePosition &pos() const { + return pos_; + } + Handle type() const { + return type_; + } + Scope *scope() const { + return scope_; + } + + public: +#define _(name) \ + bool is##name() const { \ + return (kind() == k##name); \ + } \ + name##Symbol *as##name() { \ + if (is##name()) \ + return to##name(); \ + return NULL; \ + } \ + name##Symbol *to##name() { \ + assert(is##name()); \ + return reinterpret_cast(this); \ + } + SYMBOL_KINDS(_) +#undef _ + + private: + Scope *scope_; + ScopedRoot name_; + SourcePosition pos_; + + protected: + ScopedRoot type_; +}; + +class VariableSymbol : public Symbol +{ + public: + enum Storage { + Unknown, + Local, + Arg, + Heap + }; + + public: + VariableSymbol(Scope *scope, Handle name, const SourcePosition &pos) + : Symbol(scope, name, pos), + storage_(Unknown) + { + } + + VariableSymbol(Scope *scope, Handle name, const SourcePosition &pos, Handle type) + : Symbol(scope, name, pos), + storage_(Unknown) + { + type_ = type; + } + + Kind kind() const { + return kVariable; + } + void setType(Type *type) { + assert(!type_); + type_ = type; + } + void allocate(Storage storage, unsigned slot) { + storage_ = storage; + slot_ = slot; + } + Storage storage() const { + return storage_; + } + unsigned slot() const { + assert(storage() != Unknown); + return slot_; + } + + private: + Storage storage_; + unsigned slot_; +}; + +class TypeSymbol : public Symbol +{ + public: + TypeSymbol(Scope *scope, Handle name, Handle type, Handle effective) + : Symbol(scope, name, SourcePosition()), + effective_(effective) + { + type_ = type; + } + + Kind kind() const { + return kType; + } + Handle effective() { + return effective_; + } + + private: + ScopedRoot effective_; +}; + +class FunctionSymbol : public Symbol +{ + public: + FunctionSymbol(Scope *scope, Handle name, const SourcePosition &pos, Handle type) + : Symbol(scope, name, pos) + { + type_ = type; + } + + Kind kind() const { + return kFunction; + } + FunctionType *type() const { + return FunctionType::cast(type_); + } +}; + +class ConstantSymbol : public Symbol +{ + public: + ConstantSymbol(Scope *scope, Handle name, const SourcePosition &pos, Handle type, + const BoxedPrimitive &prim) + : Symbol(scope, name, pos), + value_(prim) + { + type_ = type; + } + + Kind kind() const { + return kConstant; + } + const BoxedPrimitive &value() const { + return value_; + } + + private: + BoxedPrimitive value_; +}; + +class ImportSymbol : public Symbol +{ + public: + ImportSymbol(Scope *scope, Handle name, const SourcePosition &pos, Handle type, + unsigned index) + : Symbol(scope, name, pos), + index_(index) + { + type_ = type; + } + + Kind kind() const { + return kImport; + } + unsigned index() const { + return index_; + } + + private: + unsigned index_; +}; + +class ImplicitSymbol : public Symbol +{ + public: + ImplicitSymbol(Scope *scope, Handle name, const SourcePosition &pos, Handle type, + unsigned index) + : Symbol(scope, name, pos), + index_(index), + fieldIndex_(unsigned(-1)) + { + type_ = type; + } + + bool bound() const { + return fieldIndex_ != unsigned(-1); + } + Kind kind() const { + return kImplicit; + } + unsigned index() const { + return index_; + } + void bind(unsigned fieldIndex) { + fieldIndex_ = fieldIndex; + } + unsigned fieldIndex() const { + assert(bound()); + return fieldIndex_; + } + + private: + unsigned index_; + unsigned fieldIndex_; +}; + +} + +#endif // _include_sp2_symbol_h_ diff -r c4b1341297e5 -r ba85a47ee414 src/compiler/Token.h --- a/src/compiler/Token.h Mon Jun 10 00:46:29 2013 +0100 +++ b/src/compiler/Token.h Mon Jun 10 01:04:43 2013 +0100 @@ -1,128 +1,128 @@ -/* vim: set ts=4 sw=4 tw=99 et: - * - * Copyright (C) 2012 David Anderson - * - * This file is part of SourcePawn. - * - * SourcePawn is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * SourcePawn. If not, see http://www.gnu.org/licenses/. - */ -#ifndef _include_jitcraft_token_h_ -#define _include_jitcraft_token_h_ - -namespace ke { - -// List of tokens the scanner can recognize. -// -// Some restrictions: -// * (ASSIGN_ADD - TOK_PLUS) must be the delta between an operator and its -// op-and-assign equivalent. -#define TOKENMAP(_) \ - _(NONE, "") \ - _(ERROR, "") \ - _(NEW, "'new'") \ - _(FOR, "'for'") \ - _(RETURN, "'return'") \ - _(NAME, "") \ - _(INTEGER_LITERAL, "") \ - _(HEX_LITERAL, "") \ - _(FLOAT_LITERAL, "") \ - _(STRING_LITERAL, "") \ - _(IF, "") \ - _(ELSE, "") \ - _(ENUM, "") \ - _(LABEL, "