Skip to content

Commit

Permalink
DOM iterator implementation part 2
Browse files Browse the repository at this point in the history
In order to support the map/set like iterators generic entries, keys, values and next functions are provided. These functions will do the basic type sanity check.
Invoking entries/keys/values will return the CustomExternalIterator (a dynamic object) which will have generic next function that will be eventually call the host next function (which will actually iterate).
  • Loading branch information
akroshg committed May 1, 2017
1 parent 4aa0daf commit 58ac5ae
Show file tree
Hide file tree
Showing 9 changed files with 315 additions and 3 deletions.
5 changes: 3 additions & 2 deletions lib/Common/Exceptions/ReportError.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ enum ErrorReason
Fatal_EntryExitRecordCorruption = 16,
Fatal_UnexpectedExceptionHandling = 17,
Fatal_RpcFailure = 18,
Fatal_JsReentrancy_Error = 19,
Fatal_TTDAbort = 20
Fatal_JsReentrancy_Error = 19,
Fatal_TTDAbort = 20,
Fatal_Failed_API_Result = 21,
};

extern "C" void ReportFatalException(
Expand Down
2 changes: 2 additions & 0 deletions lib/Parser/rterrors.h
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,8 @@ RT_ERROR_MSG(WASMERR_ArrayIndexOutOfRange, 7017, "", "Memory index is out of ran
RT_ERROR_MSG(WASMERR_InvalidInstantiateArgument, 7018, "", "Invalid arguments to instantiate", kjstTypeError, 0)
RT_ERROR_MSG(WASMERR_WasmLinkError, 7019, "%s", "Linking failed.", kjstWebAssemblyLinkError, 0)
RT_ERROR_MSG(JSERR_OutOfBoundString, 7020, "", "String length is out of bound", kjstRangeError, 0)
RT_ERROR_MSG(JSERR_InvalidIterableObject, 7021, "%s : Invalid iterable object", "Invalid iterable object", kjstTypeError, 0)
RT_ERROR_MSG(JSERR_InvalidIteratorObject, 7022, "%s : Invalid iterator object", "Invalid iterator object", kjstTypeError, 0)

// Wabt Errors
RT_ERROR_MSG(WABTERR_WabtError, 7200, "%s", "Wabt Error.", kjstTypeError, 0)
2 changes: 2 additions & 0 deletions lib/Runtime/Library/Chakra.Runtime.Library.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -165,12 +165,14 @@
<ClCompile Include="$(MSBuildThisFileDirectory)WebAssemblyTable.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)WebAssemblyEnvironment.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)WabtInterface.cpp" />
<ClCompile Include="CustomExternalIterator.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\InternalPropertyList.h" />
<ClInclude Include="..\RuntimeCommon.h" />
<ClInclude Include="..\SerializableFunctionFields.h" />
<ClInclude Include="AtomicsObject.h" />
<ClInclude Include="CustomExternalIterator.h" />
<ClInclude Include="ExternalLibraryBase.h" />
<ClInclude Include="IntlEngineInterfaceExtensionObject.h" />
<ClInclude Include="JavascriptListIterator.h" />
Expand Down
2 changes: 2 additions & 0 deletions lib/Runtime/Library/Chakra.Runtime.Library.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@
<ClCompile Include="$(MSBuildThisFileDirectory)WebAssemblyTable.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)WebAssemblyEnvironment.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)WabtInterface.cpp" />
<ClCompile Include="CustomExternalIterator.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\InternalPropertyList.h" />
Expand Down Expand Up @@ -252,6 +253,7 @@
<ClInclude Include="WebAssemblyTable.h" />
<ClInclude Include="WebAssemblyEnvironment.h" />
<ClInclude Include="WabtInterface.h" />
<ClInclude Include="CustomExternalIterator.h" />
</ItemGroup>
<ItemGroup>
<None Include="ConcatString.inl" />
Expand Down
208 changes: 208 additions & 0 deletions lib/Runtime/Library/CustomExternalIterator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#include "RuntimeLibraryPch.h"

namespace Js
{
ExternalIteratorCreatorFunction::ExternalIteratorCreatorFunction(DynamicType* type,
FunctionInfo* functionInfo,
JavascriptTypeId typeId,
uint byteCount,
Var prototypeForIterator, InitIteratorFunction initFunction, NextFunction nextFunction)
: RuntimeFunction(type, functionInfo), m_externalTypeId(typeId), m_extraByteCount(byteCount),
m_prototypeForIterator(prototypeForIterator), m_initFunction(initFunction), m_nextFunction(nextFunction)
{
}

void ExternalIteratorCreatorFunction::ThrowIfNotValidObject(Var instance)
{
JavascriptTypeId typeId = (JavascriptTypeId)Js::JavascriptOperators::GetTypeId(instance);
if (typeId != m_externalTypeId || !RecyclableObject::Is(m_prototypeForIterator))
{
JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_InvalidIterableObject);
}
}

Var ExternalIteratorCreatorFunction::CreateFunction(JavascriptLibrary *library,
JavascriptTypeId typeId,
JavascriptMethod entryPoint,
uint byteCount,
Var prototypeForIterator, InitIteratorFunction initFunction, NextFunction nextFunction)
{
FunctionInfo* functionInfo = RecyclerNew(library->GetRecycler(), FunctionInfo, entryPoint);
DynamicType* type = library->CreateDeferredPrototypeFunctionType(entryPoint);
ExternalIteratorCreatorFunction* function = RecyclerNewEnumClass(library->GetRecycler(),
EnumClass_1_Bit,
ExternalIteratorCreatorFunction,
type,
functionInfo,
typeId,
byteCount,
prototypeForIterator, initFunction, nextFunction);

function->SetPropertyWithAttributes(PropertyIds::length, TaggedInt::ToVarUnchecked(0), PropertyConfigurable, nullptr);
return function;
}

Var ExternalIteratorCreatorFunction::CreateCustomExternalIterator(Var instance, ExternalIteratorCreatorFunction* function, ExternalIteratorKind kind)
{
Assert(function != nullptr);
function->ThrowIfNotValidObject(instance);
ScriptContext *scriptContext = function->GetScriptContext();

AssertOrFailFast(RecyclableObject::Is(function->m_prototypeForIterator));
DynamicObject *prototype = static_cast<DynamicObject*>(function->m_prototypeForIterator);
Js::DynamicType *type = scriptContext->GetLibrary()->CreateObjectTypeNoCache(prototype, TypeIds_ExternalIterator);

AssertOrFailFast(function->m_extraByteCount >= sizeof(void*));

CustomExternalIterator *iterator = RecyclerNewPlus(scriptContext->GetRecycler(),
function->m_extraByteCount,
CustomExternalIterator,
type,
kind,
function->m_externalTypeId,
function->m_nextFunction);

AssertOrFailFast(function->m_initFunction != nullptr);
function->m_initFunction(instance, (Var)iterator);
return iterator;
}

Var ExternalIteratorCreatorFunction::EntryExternalEntries(RecyclableObject* function, CallInfo callInfo, ...)
{
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
ARGUMENTS(args, callInfo);
if (args.Info.Flags & CallFlags_New)
{
JavascriptError::ThrowTypeError(function->GetScriptContext(), JSERR_ErrorOnNew);
}

ExternalIteratorCreatorFunction* iteratorFunction = static_cast<ExternalIteratorCreatorFunction*>(function);
return CreateCustomExternalIterator(args[0], iteratorFunction, ExternalIteratorKind::External_KeyAndValue);
}

Var ExternalIteratorCreatorFunction::EntryExternalKeys(RecyclableObject* function, CallInfo callInfo, ...)
{
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
ARGUMENTS(args, callInfo);
if (args.Info.Flags & CallFlags_New)
{
JavascriptError::ThrowTypeError(function->GetScriptContext(), JSERR_ErrorOnNew);
}

ExternalIteratorCreatorFunction* iteratorFunction = static_cast<ExternalIteratorCreatorFunction*>(function);
return CreateCustomExternalIterator(args[0], iteratorFunction, ExternalIteratorKind::External_Keys);
}

Var ExternalIteratorCreatorFunction::EntryExternalValues(RecyclableObject* function, CallInfo callInfo, ...)
{
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
ARGUMENTS(args, callInfo);
if (args.Info.Flags & CallFlags_New)
{
JavascriptError::ThrowTypeError(function->GetScriptContext(), JSERR_ErrorOnNew);
}

ExternalIteratorCreatorFunction* iteratorFunction = static_cast<ExternalIteratorCreatorFunction*>(function);
return CreateCustomExternalIterator(args[0], iteratorFunction, ExternalIteratorKind::External_Values);
}

JavascriptExternalIteratorNextFunction::JavascriptExternalIteratorNextFunction(DynamicType* type,
FunctionInfo* functionInfo,
JavascriptTypeId typeId)
: RuntimeFunction(type, functionInfo), m_externalTypeId(typeId)
{ }

JavascriptExternalIteratorNextFunction* JavascriptExternalIteratorNextFunction::CreateFunction(JavascriptLibrary *library, JavascriptTypeId typeId, JavascriptMethod entryPoint)
{
FunctionInfo* functionInfo = RecyclerNew(library->GetRecycler(), FunctionInfo, entryPoint);
DynamicType* type = library->CreateDeferredPrototypeFunctionType(entryPoint);
JavascriptExternalIteratorNextFunction* function = RecyclerNewEnumClass(library->GetRecycler(), EnumClass_1_Bit, JavascriptExternalIteratorNextFunction, type, functionInfo, typeId);

function->SetPropertyWithAttributes(PropertyIds::length, TaggedInt::ToVarUnchecked(0), PropertyConfigurable, nullptr);
return function;
}

CustomExternalIterator::CustomExternalIterator(DynamicType* type, ExternalIteratorKind kind, JavascriptTypeId typeId, NextFunction nextFunction) :
DynamicObject(type),
m_kind(kind),
m_externalTypeId(typeId),
m_nextFunction(nextFunction)
{
Assert(type->GetTypeId() == TypeIds_ExternalIterator);
}

bool CustomExternalIterator::Is(Var aValue)
{
TypeId typeId = JavascriptOperators::GetTypeId(aValue);
return typeId == TypeIds_ExternalIterator;
}

CustomExternalIterator* CustomExternalIterator::FromVar(Var aValue)
{
AssertMsg(Is(aValue), "Ensure var is actually a 'ExternalIterator'");
return static_cast<CustomExternalIterator *>(RecyclableObject::FromVar(aValue));
}

Var CustomExternalIterator::CreateNextFunction(JavascriptLibrary *library, JavascriptTypeId typeId)
{
return JavascriptExternalIteratorNextFunction::CreateFunction(library, typeId, CustomExternalIterator::EntryNext);
}

Var CustomExternalIterator::EntryNext(RecyclableObject* function, CallInfo callInfo, ...)
{
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
ARGUMENTS(args, callInfo);
ScriptContext* scriptContext = function->GetScriptContext();
if (args.Info.Flags & CallFlags_New)
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_ErrorOnNew);
}

JavascriptExternalIteratorNextFunction* iteratorNextFunction = static_cast<JavascriptExternalIteratorNextFunction*>(function);

Var thisObj = args[0];

if (!CustomExternalIterator::Is(thisObj))
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_InvalidIteratorObject, _u("Iterator.prototype.next"));
}

CustomExternalIterator * currentIterator = CustomExternalIterator::FromVar(thisObj);
if (iteratorNextFunction->GetExternalTypeId() != currentIterator->m_externalTypeId)
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_InvalidIteratorObject, _u("Iterator.prototype.next"));
}

Var key = nullptr;
Var value = nullptr;
JavascriptLibrary* library = scriptContext->GetLibrary();
if (currentIterator->m_nextFunction == nullptr || !currentIterator->m_nextFunction(currentIterator, &key, &value))
{
return library->CreateIteratorResultObjectUndefinedTrue();
}

Var result;
if (currentIterator->m_kind == ExternalIteratorKind::External_KeyAndValue)
{
JavascriptArray* keyValueTuple = library->CreateArray(2);
keyValueTuple->SetItem(0, key, PropertyOperation_None);
keyValueTuple->SetItem(1, value, PropertyOperation_None);
result = keyValueTuple;
}
else if (currentIterator->m_kind == ExternalIteratorKind::External_Keys)
{
result = key;
}
else
{
Assert(currentIterator->m_kind == ExternalIteratorKind::External_Values);
result = value;
}

return library->CreateIteratorResultObjectValueFalse(result);
}
}
95 changes: 95 additions & 0 deletions lib/Runtime/Library/CustomExternalIterator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------

namespace Js
{
typedef void(*InitIteratorFunction)(Var, Var);
typedef bool(*NextFunction)(Var, Var *, Var *);

enum class ExternalIteratorKind
{
External_Keys,
External_Values,
External_KeyAndValue,
};

class ExternalIteratorCreatorFunction : public RuntimeFunction
{
protected:
DEFINE_MARSHAL_OBJECT_TO_SCRIPT_CONTEXT(ExternalIteratorCreatorFunction);
DEFINE_VTABLE_CTOR(ExternalIteratorCreatorFunction, RuntimeFunction);

public:
ExternalIteratorCreatorFunction(DynamicType* type,
FunctionInfo* functionInfo,
JavascriptTypeId typeId,
uint byteCount,
Var prototypeForIterator, InitIteratorFunction initFunction, NextFunction nextFunction);

virtual BOOL IsExternalFunction() override { return TRUE; }

static Var EntryExternalEntries(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryExternalKeys(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryExternalValues(RecyclableObject* function, CallInfo callInfo, ...);

void ThrowIfNotValidObject(Var instance);
static Var CreateCustomExternalIterator(Var instance, ExternalIteratorCreatorFunction* function, ExternalIteratorKind kind);

static Var CreateFunction(JavascriptLibrary *library,
JavascriptTypeId typeId,
JavascriptMethod entryPoint,
uint byteCount,
Var prototypeForIterator, InitIteratorFunction initFunction, NextFunction nextFunction);

public:
Field(JavascriptTypeId) m_externalTypeId;
Field(uint) m_extraByteCount;
Field(InitIteratorFunction) m_initFunction;
Field(NextFunction) m_nextFunction;
Field(Var) m_prototypeForIterator;

friend class JavascriptLibrary;
};

class JavascriptExternalIteratorNextFunction : public RuntimeFunction
{
protected:
DEFINE_MARSHAL_OBJECT_TO_SCRIPT_CONTEXT(JavascriptExternalIteratorNextFunction);
DEFINE_VTABLE_CTOR(JavascriptExternalIteratorNextFunction, RuntimeFunction);

Field(JavascriptTypeId) m_externalTypeId;

JavascriptExternalIteratorNextFunction(DynamicType* type, FunctionInfo* functionInfo, JavascriptTypeId typeId);
public:

virtual BOOL IsExternalFunction() override { return TRUE; }
JavascriptTypeId GetExternalTypeId() const { return m_externalTypeId; }

static JavascriptExternalIteratorNextFunction* CreateFunction(JavascriptLibrary *library, JavascriptTypeId typeId, JavascriptMethod entryPoint);

friend class JavascriptLibrary;
};

class CustomExternalIterator : public DynamicObject
{
private:
Field(ExternalIteratorKind) m_kind;
Field(JavascriptTypeId) m_externalTypeId;
Field(NextFunction) m_nextFunction;

protected:
DEFINE_VTABLE_CTOR(CustomExternalIterator, DynamicObject);
DEFINE_MARSHAL_OBJECT_TO_SCRIPT_CONTEXT(CustomExternalIterator);

public:
CustomExternalIterator(DynamicType* type, ExternalIteratorKind kind, JavascriptTypeId typeId, NextFunction nextFunction);

static bool Is(Var aValue);
static CustomExternalIterator* FromVar(Var aValue);
static Var CreateNextFunction(JavascriptLibrary *library, JavascriptTypeId typeId);
static Var EntryNext(RecyclableObject* function, CallInfo callInfo, ...);
};

}
1 change: 1 addition & 0 deletions lib/Runtime/Library/JavascriptLibraryBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ namespace Js
DynamicObject* GetTypeErrorPrototype() const { return typeErrorPrototype; }
DynamicObject* GetURIErrorPrototype() const { return uriErrorPrototype; }
PropertyId GetPropertyIdSymbolIterator() { return PropertyIds::_symbolIterator; };
PropertyId GetPropertyIdSymbolToStringTag() { return PropertyIds::_symbolToStringTag; };

protected:
Field(GlobalObject*) globalObject;
Expand Down
1 change: 1 addition & 0 deletions lib/Runtime/Runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,7 @@ enum tagDEBUG_EVENT_INFO_TYPE
#include "Library/JavascriptFunction.h"
#include "Library/RuntimeFunction.h"
#include "Library/JavascriptExternalFunction.h"
#include "Library/CustomExternalIterator.h"

#include "Base/CharStringCache.h"

Expand Down
2 changes: 1 addition & 1 deletion lib/Runtime/Types/EdgeJavascriptTypeId.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,6 @@ enum TypeId
TypeIds_SpreadArgument = 79,
TypeIds_ModuleNamespace = 80,
TypeIds_ListIterator = 81,

TypeIds_ExternalIterator = 82,
TypeIds_Limit //add a new TypeId before TypeIds_Limit or before TypeIds_LastTrueJavascriptObjectType
};

0 comments on commit 58ac5ae

Please sign in to comment.