blob: ef0fb07a767889fde26ca9392f945c7c196b742d [file]
//-------------------------------------------------------------------------------------------------------
// 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"
#include "JavascriptExceptionMetadata.h"
namespace Js {
SimplePropertyDescriptor const ExceptionMetadataPropertyDescriptors[6] =
{
SimplePropertyDescriptor(NO_WRITE_BARRIER_TAG(BuiltInPropertyRecords::exception), PropertyWritable),
SimplePropertyDescriptor(NO_WRITE_BARRIER_TAG(BuiltInPropertyRecords::source), PropertyWritable),
SimplePropertyDescriptor(NO_WRITE_BARRIER_TAG(BuiltInPropertyRecords::line), PropertyWritable),
SimplePropertyDescriptor(NO_WRITE_BARRIER_TAG(BuiltInPropertyRecords::column), PropertyWritable),
SimplePropertyDescriptor(NO_WRITE_BARRIER_TAG(BuiltInPropertyRecords::length), PropertyWritable),
SimplePropertyDescriptor(NO_WRITE_BARRIER_TAG(BuiltInPropertyRecords::url), PropertyWritable)
};
SimpleTypeHandler<6> JavascriptExceptionMetadata::ExceptionMetadataTypeHandler(NO_WRITE_BARRIER_TAG(ExceptionMetadataPropertyDescriptors));
Var JavascriptExceptionMetadata::CreateMetadataVar(ScriptContext * scriptContext) {
DynamicType* exceptionMetadataType = DynamicType::New(scriptContext, TypeIds_Object,
scriptContext->GetLibrary()->GetNull(), nullptr, &JavascriptExceptionMetadata::ExceptionMetadataTypeHandler, true, true);
return DynamicObject::New(scriptContext->GetRecycler(), exceptionMetadataType);
}
void JavascriptExceptionMetadata::PopulateMetadataFromCompileException(Var metadata, Var exception, ScriptContext * scriptContext) {
Js::Var var;
var = Js::JavascriptOperators::ToNumber(
Js::JavascriptOperators::OP_GetProperty(exception, Js::PropertyIds::line, scriptContext),
scriptContext);
Js::JavascriptOperators::OP_SetProperty(metadata, Js::PropertyIds::line, var, scriptContext);
var = Js::JavascriptOperators::ToNumber(
Js::JavascriptOperators::OP_GetProperty(exception, Js::PropertyIds::column, scriptContext),
scriptContext);
Js::JavascriptOperators::OP_SetProperty(metadata, Js::PropertyIds::column, var, scriptContext);
var = Js::JavascriptOperators::ToNumber(
Js::JavascriptOperators::OP_GetProperty(exception, Js::PropertyIds::length, scriptContext),
scriptContext);
Js::JavascriptOperators::OP_SetProperty(metadata, Js::PropertyIds::length, var, scriptContext);
var = Js::JavascriptConversion::ToString(
Js::JavascriptOperators::OP_GetProperty(exception, Js::PropertyIds::source, scriptContext),
scriptContext);
Js::JavascriptOperators::OP_SetProperty(metadata, Js::PropertyIds::source, var, scriptContext);
var = Js::JavascriptConversion::ToString(
Js::JavascriptOperators::OP_GetProperty(exception, Js::PropertyIds::url, scriptContext),
scriptContext);
Js::JavascriptOperators::OP_SetProperty(metadata, Js::PropertyIds::url, var, scriptContext);
}
bool JavascriptExceptionMetadata::PopulateMetadataFromException(Var metadata, JavascriptExceptionObject * recordedException, ScriptContext * scriptContext) {
uint32 offset = recordedException->GetByteCodeOffset();
FunctionBody * functionBody = recordedException->GetFunctionBody();
ULONG line;
LONG column;
if (functionBody->GetUtf8SourceInfo()->GetIsLibraryCode() ||
!functionBody->GetLineCharOffset(offset, &line, &column)) {
line = 0;
column = 0;
}
Js::Utf8SourceInfo* sourceInfo = functionBody->GetUtf8SourceInfo();
sourceInfo->EnsureLineOffsetCache();
JsUtil::LineOffsetCache<Recycler> *cache = sourceInfo->GetLineOffsetCache();
if (line >= cache->GetLineCount())
{
return false;
}
uint32 nextLine;
if (UInt32Math::Add(line, 1, &nextLine))
{
// Overflowed
return false;
}
Js::JavascriptOperators::OP_SetProperty(metadata, Js::PropertyIds::line,
Js::JavascriptNumber::New(line, scriptContext), scriptContext);
Js::JavascriptOperators::OP_SetProperty(metadata, Js::PropertyIds::column,
Js::JavascriptNumber::New(column, scriptContext), scriptContext);
Js::JavascriptOperators::OP_SetProperty(metadata, Js::PropertyIds::length,
Js::JavascriptNumber::New(0, scriptContext), scriptContext);
Js::JavascriptOperators::OP_SetProperty(metadata, Js::PropertyIds::url,
Js::JavascriptString::NewCopySz(functionBody->GetSourceContextInfo()->url, scriptContext), scriptContext);
LPCUTF8 functionSource = sourceInfo->GetSource(_u("Jsrt::JsExperimentalGetAndClearExceptionWithMetadata"));
charcount_t startByteOffset = 0;
charcount_t endByteOffset = 0;
charcount_t startCharOffset = 0;
charcount_t endCharOffset = 0;
startCharOffset = cache->GetCharacterOffsetForLine(line, &startByteOffset);
if (nextLine >= cache->GetLineCount())
{
endByteOffset = functionBody->LengthInBytes();
endCharOffset = functionBody->LengthInChars();
}
else
{
endCharOffset = cache->GetCharacterOffsetForLine(nextLine, &endByteOffset);
// The offsets above point to the start of the following line,
// while we need to find the end of the current line.
// To do so, just step back over the preceeding newline character(s)
if (functionSource[endByteOffset - 1] == _u('\n'))
{
endCharOffset--;
endByteOffset--;
// This may have been \r\n
if (endByteOffset > 0 && functionSource[endByteOffset - 1] == _u('\r'))
{
endCharOffset--;
endByteOffset--;
}
}
else
{
utf8::DecodeOptions options = utf8::doAllowThreeByteSurrogates;
LPCUTF8 potentialNewlineStart = functionSource + endByteOffset - 3;
char16 decodedCharacter = utf8::Decode(potentialNewlineStart, functionSource + endByteOffset, options);
if (decodedCharacter == 0x2028 || decodedCharacter == 0x2029)
{
endCharOffset--;
endByteOffset -= 3;
}
else if (functionSource[endByteOffset - 1] == _u('\r'))
{
endCharOffset--;
endByteOffset--;
}
else
{
AssertMsg(FALSE, "Line ending logic out of sync between Js::JavascriptExceptionMetadata and JsUtil::LineOffsetCache::GetCharacterOffsetForLine");
return false;
}
}
}
LPCUTF8 functionStart = functionSource + startByteOffset;
Js::BufferStringBuilder builder(endCharOffset - startCharOffset, scriptContext);
utf8::DecodeOptions options = sourceInfo->IsCesu8() ? utf8::doAllowThreeByteSurrogates : utf8::doDefault;
utf8::DecodeUnitsInto(builder.DangerousGetWritableBuffer(), functionStart, functionSource + endByteOffset, options);
Js::JavascriptOperators::OP_SetProperty(metadata, Js::PropertyIds::source,
builder.ToString(), scriptContext);
return true;
}
}