mirror of
https://github.com/Samsung/escargot.git
synced 2026-06-22 10:01:50 +00:00
Introduce Object, FunctionTemplate
* Add cctest for Object, FunctionTemplates * Fix one of EncodedValue <-> EncodedSmallValue conversion bug Signed-off-by: Seonghyun Kim <sh8281.kim@samsung.com>
This commit is contained in:
parent
d3e5ec69ce
commit
ca9b832a77
26 changed files with 1686 additions and 192 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
|
@ -21,3 +21,6 @@
|
|||
[submodule "test/kangax"]
|
||||
path = test/kangax
|
||||
url = https://github.com/kangax/compat-table.git
|
||||
[submodule "third_party/googletest"]
|
||||
path = third_party/googletest
|
||||
url = https://github.com/google/googletest.git
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ SET (CMAKE_VERBOSE_MAKEFILE true)
|
|||
SET (ESCARGOT_ASAN "0" CACHE STRING "ESCARGOT_ASAN")
|
||||
|
||||
SET (ESCARGOT_TARGET escargot)
|
||||
SET (ESCARGOT_CCTEST_TARGET cctest)
|
||||
|
||||
INCLUDE (ProcessorCount)
|
||||
PROCESSORCOUNT (NPROCS)
|
||||
|
|
|
|||
8
Jenkinsfile
vendored
8
Jenkinsfile
vendored
|
|
@ -41,7 +41,8 @@ def isPr() {
|
|||
|
||||
stage('Submodule update') {
|
||||
sh 'git submodule init test/'
|
||||
sh 'git submodule init third_party/GCutil'
|
||||
sh 'git submodule init third_party/GCutil/'
|
||||
sh 'git submodule init third_party/googletest/'
|
||||
sh 'git submodule update'
|
||||
}
|
||||
|
||||
|
|
@ -65,6 +66,7 @@ def isPr() {
|
|||
'64bit' : {
|
||||
sh 'LDFLAGS=" -L/usr/icu64/lib/ -Wl,-rpath=/usr/icu64/lib/" PKG_CONFIG_PATH="/usr/icu64/lib/pkgconfig/" cmake -H./ -Bbuild/out_linux64 -DESCARGOT_HOST=linux -DESCARGOT_ARCH=x64 -DESCARGOT_MODE=debug -DESCARGOT_OUTPUT=shell_test -GNinja'
|
||||
sh 'LDFLAGS=" -L/usr/icu64/lib/ -Wl,-rpath=/usr/icu64/lib/" PKG_CONFIG_PATH="/usr/icu64/lib/pkgconfig/" cmake -H./ -Bbuild/out_linux64_release -DESCARGOT_HOST=linux -DESCARGOT_ARCH=x64 -DESCARGOT_MODE=release -DESCARGOT_OUTPUT=shell_test -GNinja'
|
||||
sh 'LDFLAGS=" -L/usr/icu64/lib/ -Wl,-rpath=/usr/icu64/lib/" PKG_CONFIG_PATH="/usr/icu64/lib/pkgconfig/" cmake -H./ -Bbuild/out_linux64_cctest -DESCARGOT_HOST=linux -DESCARGOT_ARCH=x64 -DESCARGOT_MODE=debug -DESCARGOT_OUTPUT=cctest -GNinja'
|
||||
},
|
||||
'debugger-binary' : {
|
||||
sh 'cmake -H./ -Bbuild/debugger_out_linux64 -DESCARGOT_HOST=linux -DESCARGOT_ARCH=x64 -DESCARGOT_MODE=debug -DESCARGOT_DEBUGGER=1 -DESCARGOT_OUTPUT=shell_test -GNinja'
|
||||
|
|
@ -77,6 +79,7 @@ def isPr() {
|
|||
sh 'cd build/out_linux64/; ninja'
|
||||
sh 'cd build/out_linux_release/; ninja'
|
||||
sh 'cd build/out_linux64_release/; ninja'
|
||||
sh 'cd build/out_linux64_cctest/; ninja'
|
||||
sh 'cd build/debugger_out_linux64/; ninja'
|
||||
}
|
||||
|
||||
|
|
@ -124,6 +127,9 @@ def isPr() {
|
|||
'kangax test-suites' : {
|
||||
sh 'python tools/kangax/run-kangax.py --engine="${WORKSPACE}/build/out_linux64/escargot"'
|
||||
},
|
||||
'cctest': {
|
||||
sh './tools/run-tests.py cctest --engine=${WORKSPACE}/build/out_linux64_cctest/cctest'
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ The following build options are supported when generating ninja rules using cmak
|
|||
Compile Escargot for each architecture
|
||||
* -DESCARGOT_MODE=[ debug | release ]<br>
|
||||
Compile Escargot for either release or debug mode
|
||||
* -DESCARGOT_OUTPUT=[ shared_lib | static_lib | shell | shell_test ]<br>
|
||||
* -DESCARGOT_OUTPUT=[ shared_lib | static_lib | shell | shell_test | cctest ]<br>
|
||||
Define target output type
|
||||
* -DESCARGOT_LIBICU_SUPPORT=[ ON | OFF ]<br>
|
||||
Enable libicu library if set ON. (Optional, default = ON)
|
||||
|
|
|
|||
|
|
@ -57,11 +57,17 @@ IF (NOT ${ESCARGOT_OUTPUT} MATCHES "shell")
|
|||
LIST (REMOVE_ITEM ESCARGOT_SRC ${ESCARGOT_ROOT}/src/shell/Shell.cpp)
|
||||
ENDIF()
|
||||
|
||||
IF (${ESCARGOT_OUTPUT} STREQUAL "cctest")
|
||||
ADD_SUBDIRECTORY (third_party/googletest/googletest)
|
||||
FILE (GLOB CCTEST_SRC ${ESCARGOT_ROOT}/test/cctest/testapi.cpp)
|
||||
ENDIF()
|
||||
|
||||
SET (ESCARGOT_SRC_LIST
|
||||
${ESCARGOT_SRC}
|
||||
${YARR_SRC}
|
||||
${DOUBLE_CONVERSION_SRC}
|
||||
${LZ4_SRC}
|
||||
${CCTEST_SRC}
|
||||
)
|
||||
|
||||
# GCUTIL
|
||||
|
|
@ -118,4 +124,12 @@ ELSEIF (${ESCARGOT_OUTPUT} STREQUAL "static_lib")
|
|||
TARGET_INCLUDE_DIRECTORIES (${ESCARGOT_TARGET} PUBLIC ${ESCARGOT_INCDIRS})
|
||||
TARGET_COMPILE_DEFINITIONS (${ESCARGOT_TARGET} PUBLIC ${ESCARGOT_DEFINITIONS})
|
||||
TARGET_COMPILE_OPTIONS (${ESCARGOT_TARGET} PUBLIC ${ESCARGOT_CXXFLAGS} ${CXXFLAGS_FROM_ENV})
|
||||
|
||||
ELSEIF (${ESCARGOT_OUTPUT} STREQUAL "cctest")
|
||||
ADD_EXECUTABLE (${ESCARGOT_CCTEST_TARGET} ${ESCARGOT_SRC_LIST})
|
||||
|
||||
TARGET_LINK_LIBRARIES (${ESCARGOT_CCTEST_TARGET} ${ESCARGOT_LIBRARIES} ${ESCARGOT_LDFLAGS} ${LDFLAGS_FROM_ENV} gtest)
|
||||
TARGET_INCLUDE_DIRECTORIES (${ESCARGOT_CCTEST_TARGET} PUBLIC ${ESCARGOT_INCDIRS})
|
||||
TARGET_COMPILE_DEFINITIONS (${ESCARGOT_CCTEST_TARGET} PUBLIC ${ESCARGOT_DEFINITIONS})
|
||||
TARGET_COMPILE_OPTIONS (${ESCARGOT_CCTEST_TARGET} PUBLIC ${ESCARGOT_CXXFLAGS} -I${ESCARGOT_ROOT}/third_party/googletest/googletest/include/ ${CXXFLAGS_FROM_ENV})
|
||||
ENDIF()
|
||||
|
|
|
|||
|
|
@ -52,7 +52,11 @@
|
|||
#include "runtime/WeakMapObject.h"
|
||||
#include "runtime/GlobalObjectProxyObject.h"
|
||||
#include "runtime/CompressibleString.h"
|
||||
#include "runtime/Template.h"
|
||||
#include "runtime/ObjectTemplate.h"
|
||||
#include "runtime/FunctionTemplate.h"
|
||||
#include "interpreter/ByteCode.h"
|
||||
#include "api/internal/ValueAdapter.h"
|
||||
|
||||
namespace Escargot {
|
||||
|
||||
|
|
@ -111,21 +115,12 @@ DEFINE_CAST(SetObject);
|
|||
DEFINE_CAST(WeakSetObject);
|
||||
DEFINE_CAST(MapObject);
|
||||
DEFINE_CAST(WeakMapObject);
|
||||
DEFINE_CAST(Template);
|
||||
DEFINE_CAST(ObjectTemplate);
|
||||
DEFINE_CAST(FunctionTemplate);
|
||||
|
||||
#undef DEFINE_CAST
|
||||
|
||||
inline ValueRef* toRef(const Value& v)
|
||||
{
|
||||
ASSERT(!v.isEmpty());
|
||||
return reinterpret_cast<ValueRef*>(EncodedValue(v).payload());
|
||||
}
|
||||
|
||||
inline Value toImpl(const ValueRef* v)
|
||||
{
|
||||
ASSERT(v);
|
||||
return Value(EncodedValue::fromPayload(v));
|
||||
}
|
||||
|
||||
inline OptionalRef<ValueRef> toOptionalValue(const Value& v)
|
||||
{
|
||||
return OptionalRef<ValueRef>(reinterpret_cast<ValueRef*>(EncodedValue(v).payload()));
|
||||
|
|
@ -1084,7 +1079,6 @@ bool ObjectRef::defineNativeDataAccessorProperty(ExecutionStateRef* state, Value
|
|||
} else {
|
||||
innerData->m_setter = [](ExecutionState& state, Object* self, EncodedValue& privateDataFromObjectPrivateArea, const Value& setterInputData) -> bool {
|
||||
NativeDataAccessorPropertyData* publicData = reinterpret_cast<NativeDataAccessorPropertyData*>(privateDataFromObjectPrivateArea.payload());
|
||||
// ExecutionStateRef* state, ObjectRef* self, NativeDataAccessorPropertyData* data, ValueRef* setterInputData
|
||||
return publicData->m_setter(toRef(&state), toRef(self), publicData, toRef(setterInputData));
|
||||
};
|
||||
}
|
||||
|
|
@ -2579,6 +2573,141 @@ bool WeakMapObjectRef::has(ExecutionStateRef* state, ObjectRef* key)
|
|||
return toImpl(this)->has(*toImpl(state), toImpl(key));
|
||||
}
|
||||
|
||||
void TemplateRef::set(const TemplatePropertyNameRef& name, ValueRef* data, bool isWritable, bool isEnumerable, bool isConfigurable)
|
||||
{
|
||||
toImpl(this)->set(TemplatePropertyName(toImpl(name.value())), toImpl(data), isWritable, isEnumerable, isConfigurable);
|
||||
}
|
||||
|
||||
void TemplateRef::set(const TemplatePropertyNameRef& name, TemplateRef* data, bool isWritable, bool isEnumerable, bool isConfigurable)
|
||||
{
|
||||
toImpl(this)->set(TemplatePropertyName(toImpl(name.value())), toImpl(data), isWritable, isEnumerable, isConfigurable);
|
||||
}
|
||||
|
||||
void TemplateRef::setAccessorProperty(const TemplatePropertyNameRef& name, OptionalRef<FunctionTemplateRef> getter, OptionalRef<FunctionTemplateRef> setter, bool isEnumerable, bool isConfigurable)
|
||||
{
|
||||
Optional<FunctionTemplate*> getterImpl(toImpl(getter.get()));
|
||||
Optional<FunctionTemplate*> setterImpl(toImpl(setter.get()));
|
||||
|
||||
toImpl(this)->setAccessorProperty(TemplatePropertyName(toImpl(name.value())), getterImpl, setterImpl, isEnumerable, isConfigurable);
|
||||
}
|
||||
|
||||
void TemplateRef::setNativeDataAccessorProperty(const TemplatePropertyNameRef& name, ObjectRef::NativeDataAccessorPropertyGetter getter, ObjectRef::NativeDataAccessorPropertySetter setter,
|
||||
bool isWritable, bool isEnumerable, bool isConfigurable)
|
||||
{
|
||||
ObjectRef::NativeDataAccessorPropertyData* publicData = new ObjectRef::NativeDataAccessorPropertyData(isWritable, isEnumerable, isConfigurable, getter, setter);
|
||||
|
||||
ObjectPropertyNativeGetterSetterData* innerData = new ObjectPropertyNativeGetterSetterData(isWritable, isEnumerable, isConfigurable, [](ExecutionState& state, Object* self, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ObjectRef::NativeDataAccessorPropertyData* publicData = reinterpret_cast<ObjectRef::NativeDataAccessorPropertyData*>(privateDataFromObjectPrivateArea.payload());
|
||||
return toImpl(publicData->m_getter(toRef(&state), toRef(self), publicData));
|
||||
},
|
||||
nullptr);
|
||||
if (!isWritable) {
|
||||
innerData->m_setter = nullptr;
|
||||
} else if (publicData->m_isWritable && !publicData->m_setter) {
|
||||
innerData->m_setter = [](ExecutionState& state, Object* self, EncodedValue& privateDataFromObjectPrivateArea, const Value& setterInputData) -> bool {
|
||||
return false;
|
||||
};
|
||||
} else {
|
||||
innerData->m_setter = [](ExecutionState& state, Object* self, EncodedValue& privateDataFromObjectPrivateArea, const Value& setterInputData) -> bool {
|
||||
ObjectRef::NativeDataAccessorPropertyData* publicData = reinterpret_cast<ObjectRef::NativeDataAccessorPropertyData*>(privateDataFromObjectPrivateArea.payload());
|
||||
return publicData->m_setter(toRef(&state), toRef(self), publicData, toRef(setterInputData));
|
||||
};
|
||||
}
|
||||
|
||||
toImpl(this)->setNativeDataAccessorProperty(TemplatePropertyName(toImpl(name.value())), innerData, publicData);
|
||||
}
|
||||
|
||||
void TemplateRef::setNativeDataAccessorProperty(const TemplatePropertyNameRef& name, ObjectRef::NativeDataAccessorPropertyData* publicData)
|
||||
{
|
||||
ObjectPropertyNativeGetterSetterData* innerData = new ObjectPropertyNativeGetterSetterData(publicData->m_isWritable, publicData->m_isEnumerable, publicData->m_isConfigurable,
|
||||
[](ExecutionState& state, Object* self, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ObjectRef::NativeDataAccessorPropertyData* publicData = reinterpret_cast<ObjectRef::NativeDataAccessorPropertyData*>(privateDataFromObjectPrivateArea.payload());
|
||||
return toImpl(publicData->m_getter(toRef(&state), toRef(self), publicData));
|
||||
},
|
||||
nullptr);
|
||||
if (!publicData->m_isWritable) {
|
||||
innerData->m_setter = nullptr;
|
||||
} else if (publicData->m_isWritable && !publicData->m_setter) {
|
||||
innerData->m_setter = [](ExecutionState& state, Object* self, EncodedValue& privateDataFromObjectPrivateArea, const Value& setterInputData) -> bool {
|
||||
return false;
|
||||
};
|
||||
} else {
|
||||
innerData->m_setter = [](ExecutionState& state, Object* self, EncodedValue& privateDataFromObjectPrivateArea, const Value& setterInputData) -> bool {
|
||||
ObjectRef::NativeDataAccessorPropertyData* publicData = reinterpret_cast<ObjectRef::NativeDataAccessorPropertyData*>(privateDataFromObjectPrivateArea.payload());
|
||||
return publicData->m_setter(toRef(&state), toRef(self), publicData, toRef(setterInputData));
|
||||
};
|
||||
}
|
||||
|
||||
toImpl(this)->setNativeDataAccessorProperty(TemplatePropertyName(toImpl(name.value())), innerData, publicData);
|
||||
}
|
||||
|
||||
bool TemplateRef::has(const TemplatePropertyNameRef& name)
|
||||
{
|
||||
return toImpl(this)->has(TemplatePropertyName(toImpl(name.value())));
|
||||
}
|
||||
|
||||
bool TemplateRef::remove(const TemplatePropertyNameRef& name)
|
||||
{
|
||||
return toImpl(this)->remove(TemplatePropertyName(toImpl(name.value())));
|
||||
}
|
||||
|
||||
ObjectRef* TemplateRef::instantiate(ContextRef* ctx)
|
||||
{
|
||||
return toRef(toImpl(this)->instantiate(toImpl(ctx)));
|
||||
}
|
||||
|
||||
bool TemplateRef::didInstantiate()
|
||||
{
|
||||
return toImpl(this)->didInstantiate();
|
||||
}
|
||||
|
||||
ObjectTemplateRef* ObjectTemplateRef::create()
|
||||
{
|
||||
return toRef(new ObjectTemplate());
|
||||
}
|
||||
|
||||
FunctionTemplateRef* FunctionTemplateRef::create(AtomicStringRef* name, size_t argumentCount, bool isStrict, bool isConstructor,
|
||||
FunctionObjectRef::NativeFunctionPointer fn, OptionalRef<ObjectTemplateRef> instanceTemplate)
|
||||
{
|
||||
return toRef(new FunctionTemplate(toImpl(name), argumentCount, isStrict, isConstructor, fn, toImpl(instanceTemplate.value())));
|
||||
}
|
||||
|
||||
ObjectTemplateRef* FunctionTemplateRef::prototypeTemplate()
|
||||
{
|
||||
return toRef(toImpl(this)->prototypeTemplate());
|
||||
}
|
||||
|
||||
OptionalRef<ObjectTemplateRef> FunctionTemplateRef::instanceTemplate()
|
||||
{
|
||||
if (toImpl(this)->instanceTemplate()) {
|
||||
return toRef(toImpl(this)->instanceTemplate().value());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void FunctionTemplateRef::setInstanceTemplate(OptionalRef<ObjectTemplateRef> s)
|
||||
{
|
||||
if (s) {
|
||||
toImpl(this)->setInstanceTemplate(toImpl(s.value()));
|
||||
} else {
|
||||
toImpl(this)->setInstanceTemplate(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void FunctionTemplateRef::inherit(OptionalRef<FunctionTemplateRef> parent)
|
||||
{
|
||||
toImpl(this)->inherit(toImpl(parent.value()));
|
||||
}
|
||||
|
||||
OptionalRef<FunctionTemplateRef> FunctionTemplateRef::parent()
|
||||
{
|
||||
if (toImpl(this)->parent()) {
|
||||
return toRef(toImpl(this)->parent().value());
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ScriptParserRef::InitializeScriptResult::InitializeScriptResult()
|
||||
: script()
|
||||
, parseErrorMessage(StringRef::emptyString())
|
||||
|
|
|
|||
|
|
@ -84,6 +84,9 @@ class ScriptParserRef;
|
|||
class ExecutionStateRef;
|
||||
class ValueVectorRef;
|
||||
class JobRef;
|
||||
class TemplateRef;
|
||||
class ObjectTemplateRef;
|
||||
class FunctionTemplateRef;
|
||||
|
||||
class ESCARGOT_EXPORT Globals {
|
||||
public:
|
||||
|
|
@ -1403,6 +1406,66 @@ public:
|
|||
void set(ExecutionStateRef* state, ObjectRef* key, ValueRef* value);
|
||||
};
|
||||
|
||||
class ESCARGOT_EXPORT TemplatePropertyNameRef {
|
||||
public:
|
||||
TemplatePropertyNameRef(StringRef* name)
|
||||
: m_ptr(name)
|
||||
{
|
||||
}
|
||||
TemplatePropertyNameRef(SymbolRef* name)
|
||||
: m_ptr(name)
|
||||
{
|
||||
}
|
||||
|
||||
PointerValueRef* value() const
|
||||
{
|
||||
return m_ptr;
|
||||
}
|
||||
|
||||
private:
|
||||
PointerValueRef* m_ptr;
|
||||
};
|
||||
|
||||
// don't modify template after instantiate object
|
||||
// it is not intented operation
|
||||
class ESCARGOT_EXPORT TemplateRef {
|
||||
public:
|
||||
void set(const TemplatePropertyNameRef& name, ValueRef* data, bool isWritable, bool isEnumerable, bool isConfigurable);
|
||||
void set(const TemplatePropertyNameRef& name, TemplateRef* data, bool isWritable, bool isEnumerable, bool isConfigurable);
|
||||
void setAccessorProperty(const TemplatePropertyNameRef& name, OptionalRef<FunctionTemplateRef> getter, OptionalRef<FunctionTemplateRef> setter, bool isEnumerable, bool isConfigurable);
|
||||
void setNativeDataAccessorProperty(const TemplatePropertyNameRef& name, ObjectRef::NativeDataAccessorPropertyGetter getter, ObjectRef::NativeDataAccessorPropertySetter setter,
|
||||
bool isWritable, bool isEnumerable, bool isConfigurable);
|
||||
void setNativeDataAccessorProperty(const TemplatePropertyNameRef& name, ObjectRef::NativeDataAccessorPropertyData* data);
|
||||
|
||||
bool has(const TemplatePropertyNameRef& name);
|
||||
// return true if removed
|
||||
bool remove(const TemplatePropertyNameRef& name);
|
||||
|
||||
ObjectRef* instantiate(ContextRef* ctx);
|
||||
bool didInstantiate();
|
||||
};
|
||||
|
||||
class ESCARGOT_EXPORT ObjectTemplateRef : public TemplateRef {
|
||||
public:
|
||||
static ObjectTemplateRef* create();
|
||||
};
|
||||
|
||||
// FunctionTemplateRef returns the unique function instance in context.
|
||||
class ESCARGOT_EXPORT FunctionTemplateRef : public TemplateRef {
|
||||
public:
|
||||
static FunctionTemplateRef* create(AtomicStringRef* name, size_t argumentCount, bool isStrict, bool isConstructor,
|
||||
FunctionObjectRef::NativeFunctionPointer fn, OptionalRef<ObjectTemplateRef> instanceTemplate);
|
||||
|
||||
ObjectTemplateRef* prototypeTemplate();
|
||||
|
||||
// if there is no instance template, we will use default object
|
||||
OptionalRef<ObjectTemplateRef> instanceTemplate();
|
||||
void setInstanceTemplate(OptionalRef<ObjectTemplateRef> s);
|
||||
|
||||
void inherit(OptionalRef<FunctionTemplateRef> parent);
|
||||
OptionalRef<FunctionTemplateRef> parent();
|
||||
};
|
||||
|
||||
class ESCARGOT_EXPORT ScriptParserRef {
|
||||
public:
|
||||
struct InitializeScriptResult {
|
||||
|
|
|
|||
44
src/api/internal/ValueAdapter.h
Normal file
44
src/api/internal/ValueAdapter.h
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (c) 2020-present Samsung Electronics Co., Ltd
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#ifndef __EscargotValueAdapter__
|
||||
#define __EscargotValueAdapter__
|
||||
|
||||
#include "runtime/Value.h"
|
||||
#include "runtime/EncodedValue.h"
|
||||
|
||||
namespace Escargot {
|
||||
|
||||
class ValueRef;
|
||||
|
||||
inline ValueRef* toRef(const Value& v)
|
||||
{
|
||||
ASSERT(!v.isEmpty());
|
||||
return reinterpret_cast<ValueRef*>(EncodedValue(v).payload());
|
||||
}
|
||||
|
||||
inline Value toImpl(const ValueRef* v)
|
||||
{
|
||||
ASSERT(v);
|
||||
return Value(EncodedValue::fromPayload(v));
|
||||
}
|
||||
|
||||
} // namespace Escargot
|
||||
|
||||
#endif
|
||||
|
|
@ -39,6 +39,7 @@ class ControlFlowRecord;
|
|||
class SandBox;
|
||||
class ByteCodeBlock;
|
||||
class ToStringRecursionPreventer;
|
||||
class FunctionTemplate;
|
||||
class Debugger;
|
||||
|
||||
struct IdentifierRecord {
|
||||
|
|
@ -73,6 +74,9 @@ typedef std::unordered_map<AtomicString, GlobalVariableAccessCacheItem*, std::ha
|
|||
GCUtil::gc_malloc_allocator<std::pair<AtomicString const, GlobalVariableAccessCacheItem*>>>
|
||||
GlobalVariableAccessCache;
|
||||
|
||||
typedef Vector<std::pair<FunctionTemplate*, FunctionObject*>, GCUtil::gc_malloc_allocator<std::pair<FunctionTemplate*, FunctionObject*>>>
|
||||
InstantiatedFunctionObjects;
|
||||
|
||||
class Context : public gc {
|
||||
friend class AtomicString;
|
||||
friend class SandBox;
|
||||
|
|
@ -266,6 +270,11 @@ public:
|
|||
return m_regexpStatus;
|
||||
}
|
||||
|
||||
InstantiatedFunctionObjects& instantiatedFunctionObjects()
|
||||
{
|
||||
return m_instantiatedFunctionObjects;
|
||||
}
|
||||
|
||||
#ifdef ESCARGOT_DEBUGGER
|
||||
Debugger* debugger()
|
||||
{
|
||||
|
|
@ -321,6 +330,8 @@ private:
|
|||
// contains the result of the last matched regular expressions
|
||||
RegExpStatus m_regexpStatus;
|
||||
|
||||
InstantiatedFunctionObjects m_instantiatedFunctionObjects;
|
||||
|
||||
#ifdef ESCARGOT_DEBUGGER
|
||||
// debugger support
|
||||
Debugger* m_debugger;
|
||||
|
|
|
|||
|
|
@ -498,11 +498,13 @@ using ObjectPropertyValue = EncodedSmallValue;
|
|||
typedef ObjectPropertyValue EncodedValueVectorElement;
|
||||
typedef Vector<EncodedValueVectorElement, CustomAllocator<EncodedValueVectorElement>> EncodedValueVector;
|
||||
typedef TightVector<EncodedValueVectorElement, CustomAllocator<EncodedValueVectorElement>> EncodedValueTightVector;
|
||||
typedef TightVectorWithNoSize<EncodedSmallValue, CustomAllocator<EncodedSmallValue>> ObjectPropertyValueVector;
|
||||
#else
|
||||
using ObjectPropertyValue = EncodedValue;
|
||||
typedef ObjectPropertyValue EncodedValueVectorElement;
|
||||
typedef Vector<EncodedValueVectorElement, GCUtil::gc_malloc_allocator<EncodedValueVectorElement>> EncodedValueVector;
|
||||
typedef TightVector<EncodedValueVectorElement, GCUtil::gc_malloc_allocator<EncodedValueVectorElement>> EncodedValueTightVector;
|
||||
typedef TightVectorWithNoSizeUseGCRealloc<EncodedValue> ObjectPropertyValueVector;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -70,6 +70,14 @@ FunctionObject::FunctionObject(ExecutionState& state, Object* proto, size_t defa
|
|||
{
|
||||
}
|
||||
|
||||
FunctionObject::FunctionObject(ObjectStructure* structure, ObjectPropertyValueVector&& values, Object* proto)
|
||||
: Object(structure, std::move(values), proto)
|
||||
#ifndef NDEBUG
|
||||
, m_codeBlock((CodeBlock*)SIZE_MAX)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
FunctionObject::FunctionSource FunctionObject::createFunctionSourceFromScriptSource(ExecutionState& state, AtomicString functionName, size_t argumentValueArrayCount, Value* argumentValueArray, Value bodyString, bool useStrict, bool isGenerator, bool isAsync, bool allowSuperCall)
|
||||
{
|
||||
StringBuilder src, srcToTest;
|
||||
|
|
|
|||
|
|
@ -143,6 +143,7 @@ public:
|
|||
|
||||
protected:
|
||||
FunctionObject(ExecutionState& state, Object* proto, size_t defaultSpace); // function for derived classes. derived class MUST initlize member variable of FunctionObject.
|
||||
FunctionObject(ObjectStructure* structure, ObjectPropertyValueVector&& values, Object* proto); // ctor for FunctionTemplate
|
||||
|
||||
void initStructureAndValues(ExecutionState& state, bool isConstructor, bool isGenerator, bool isAsync);
|
||||
virtual size_t functionPrototypeIndex()
|
||||
|
|
|
|||
213
src/runtime/FunctionTemplate.cpp
Normal file
213
src/runtime/FunctionTemplate.cpp
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
/*
|
||||
* Copyright (c) 2020-present Samsung Electronics Co., Ltd
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include "Escargot.h"
|
||||
#include "FunctionTemplate.h"
|
||||
#include "runtime/Context.h"
|
||||
#include "runtime/NativeFunctionObject.h"
|
||||
#include "runtime/SandBox.h"
|
||||
#include "api/internal/ValueAdapter.h"
|
||||
|
||||
namespace Escargot {
|
||||
|
||||
void* FunctionTemplate::operator new(size_t size)
|
||||
{
|
||||
static bool typeInited = false;
|
||||
static GC_descr descr;
|
||||
if (!typeInited) {
|
||||
GC_word objBitmap[GC_BITMAP_SIZE(FunctionTemplate)] = { 0 };
|
||||
Template::fillGCDescriptor(objBitmap);
|
||||
GC_set_bit(objBitmap, GC_WORD_OFFSET(FunctionTemplate, m_nativeFunctionData));
|
||||
GC_set_bit(objBitmap, GC_WORD_OFFSET(FunctionTemplate, m_prototypeTemplate));
|
||||
GC_set_bit(objBitmap, GC_WORD_OFFSET(FunctionTemplate, m_instanceTemplate));
|
||||
GC_set_bit(objBitmap, GC_WORD_OFFSET(FunctionTemplate, m_parent));
|
||||
descr = GC_make_descriptor(objBitmap, GC_WORD_LEN(FunctionTemplate));
|
||||
typeInited = true;
|
||||
}
|
||||
return GC_MALLOC_EXPLICITLY_TYPED(size, descr);
|
||||
}
|
||||
|
||||
class CallFunctionTemplateFunctionData : public CallNativeFunctionData {
|
||||
public:
|
||||
NativeFunctionPointer m_callback;
|
||||
FunctionTemplate* m_functionTemplate;
|
||||
};
|
||||
|
||||
FunctionTemplate::FunctionTemplate(AtomicString name, size_t argumentCount, bool isStrict, bool isConstructor,
|
||||
NativeFunctionPointer fn, Optional<ObjectTemplate*> instanceTemplate)
|
||||
: m_name(name)
|
||||
, m_argumentCount(argumentCount)
|
||||
, m_isStrict(isStrict)
|
||||
, m_isConstructor(isConstructor)
|
||||
, m_prototypeTemplate(new ObjectTemplate())
|
||||
, m_instanceTemplate(instanceTemplate)
|
||||
{
|
||||
auto fnData = new CallFunctionTemplateFunctionData();
|
||||
fnData->m_functionTemplate = this;
|
||||
fnData->m_callback = fn;
|
||||
fnData->m_fn = [](ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget) -> Value {
|
||||
FunctionObject* activeFunction = state.resolveCallee();
|
||||
CallFunctionTemplateFunctionData* data = (CallFunctionTemplateFunctionData*)activeFunction->codeBlock()->nativeFunctionData();
|
||||
if (newTarget) {
|
||||
Object* ret;
|
||||
if (data->m_functionTemplate->instanceTemplate()) {
|
||||
ret = data->m_functionTemplate->instanceTemplate()->instantiate(state.context());
|
||||
} else {
|
||||
ret = new Object(state);
|
||||
}
|
||||
ret->setPrototype(state, activeFunction->getFunctionPrototype(state));
|
||||
return ret;
|
||||
} else {
|
||||
return data->m_callback(state, thisValue, argc, argv, newTarget);
|
||||
}
|
||||
};
|
||||
m_nativeFunctionData = fnData;
|
||||
}
|
||||
|
||||
class CallFunctionTemplatePublicFunctionData : public CallNativeFunctionData {
|
||||
public:
|
||||
FunctionObjectRef::NativeFunctionPointer m_callback;
|
||||
FunctionTemplate* m_functionTemplate;
|
||||
};
|
||||
|
||||
FunctionTemplate::FunctionTemplate(AtomicString name, size_t argumentCount, bool isStrict, bool isConstructor,
|
||||
FunctionObjectRef::NativeFunctionPointer fn, Optional<ObjectTemplate*> instanceTemplate)
|
||||
: m_name(name)
|
||||
, m_argumentCount(argumentCount)
|
||||
, m_isStrict(isStrict)
|
||||
, m_isConstructor(isConstructor)
|
||||
, m_prototypeTemplate(new ObjectTemplate())
|
||||
, m_instanceTemplate(instanceTemplate)
|
||||
{
|
||||
auto fnData = new CallFunctionTemplatePublicFunctionData();
|
||||
fnData->m_functionTemplate = this;
|
||||
fnData->m_callback = fn;
|
||||
fnData->m_fn = [](ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget) -> Value {
|
||||
FunctionObject* activeFunction = state.resolveCallee();
|
||||
CallFunctionTemplatePublicFunctionData* data = (CallFunctionTemplatePublicFunctionData*)activeFunction->codeBlock()->nativeFunctionData();
|
||||
if (newTarget) {
|
||||
Object* ret;
|
||||
if (data->m_functionTemplate->instanceTemplate()) {
|
||||
ret = data->m_functionTemplate->instanceTemplate()->instantiate(state.context());
|
||||
} else {
|
||||
ret = new Object(state);
|
||||
}
|
||||
ret->setPrototype(state, activeFunction->getFunctionPrototype(state));
|
||||
return ret;
|
||||
} else {
|
||||
ValueRef** newArgv = ALLOCA(sizeof(ValueRef*) * argc, ValueRef*, state);
|
||||
for (size_t i = 0; i < argc; i++) {
|
||||
newArgv[i] = toRef(argv[i]);
|
||||
}
|
||||
return toImpl(data->m_callback((ExecutionStateRef*)(&state), toRef(thisValue), argc, newArgv, newTarget.hasValue()));
|
||||
}
|
||||
};
|
||||
m_nativeFunctionData = fnData;
|
||||
}
|
||||
|
||||
Object* FunctionTemplate::instantiate(Context* ctx)
|
||||
{
|
||||
auto& instantiatedFunctionObjects = ctx->instantiatedFunctionObjects();
|
||||
for (size_t i = 0; i < instantiatedFunctionObjects.size(); i++) {
|
||||
if (instantiatedFunctionObjects[i].first == this) {
|
||||
return instantiatedFunctionObjects[i].second;
|
||||
}
|
||||
}
|
||||
|
||||
const size_t functionDefaultPropertyCount = m_isConstructor ? ctx->defaultStructureForFunctionObject()->propertyCount() : ctx->defaultStructureForNotConstructorFunctionObject()->propertyCount();
|
||||
size_t propertyCount = m_properties.size() + functionDefaultPropertyCount;
|
||||
if (!m_cachedObjectStructure) {
|
||||
if (m_isConstructor) {
|
||||
// [prototype, name, length]
|
||||
ASSERT(functionDefaultPropertyCount == 3);
|
||||
ObjectStructureItem structureItemVector[3] = {
|
||||
ObjectStructureItem(ctx->staticStrings().prototype,
|
||||
ObjectStructurePropertyDescriptor::createDataDescriptor(ObjectStructurePropertyDescriptor::WritablePresent)),
|
||||
ObjectStructureItem(ctx->staticStrings().name,
|
||||
ObjectStructurePropertyDescriptor::createDataDescriptor(ObjectStructurePropertyDescriptor::ConfigurablePresent)),
|
||||
ObjectStructureItem(ctx->staticStrings().length,
|
||||
ObjectStructurePropertyDescriptor::createDataDescriptor(ObjectStructurePropertyDescriptor::ConfigurablePresent))
|
||||
|
||||
};
|
||||
m_cachedObjectStructure = constructObjectStructure(ctx, structureItemVector, 3);
|
||||
} else {
|
||||
// [name, length]
|
||||
ASSERT(functionDefaultPropertyCount == 2);
|
||||
ObjectStructureItem structureItemVector[2] = {
|
||||
ObjectStructureItem(ctx->staticStrings().name,
|
||||
ObjectStructurePropertyDescriptor::createDataDescriptor(ObjectStructurePropertyDescriptor::ConfigurablePresent)),
|
||||
ObjectStructureItem(ctx->staticStrings().length,
|
||||
ObjectStructurePropertyDescriptor::createDataDescriptor(ObjectStructurePropertyDescriptor::ConfigurablePresent))
|
||||
|
||||
};
|
||||
|
||||
m_cachedObjectStructure = constructObjectStructure(ctx, structureItemVector, 2);
|
||||
}
|
||||
}
|
||||
|
||||
ObjectPropertyValueVector objectPropertyValues;
|
||||
Object* functionPrototype = nullptr;
|
||||
if (m_isConstructor) {
|
||||
// [prototype, name, length]
|
||||
if (!m_prototypeTemplate->has(ctx->staticStrings().constructor)) {
|
||||
m_prototypeTemplate->set(ctx->staticStrings().constructor, Value(), true, false, true);
|
||||
}
|
||||
|
||||
ObjectPropertyValue baseValues[3];
|
||||
baseValues[0] = functionPrototype = m_prototypeTemplate->instantiate(ctx);
|
||||
baseValues[1] = m_name.string();
|
||||
baseValues[2] = Value(m_argumentCount);
|
||||
constructObjectPropertyValues(ctx, baseValues, 3, objectPropertyValues);
|
||||
} else {
|
||||
// [name, length]
|
||||
ObjectPropertyValue baseValues[2];
|
||||
objectPropertyValues[0] = m_name.string();
|
||||
objectPropertyValues[1] = Value(m_argumentCount);
|
||||
constructObjectPropertyValues(ctx, baseValues, 2, objectPropertyValues);
|
||||
}
|
||||
|
||||
CodeBlock* codeBlock = new CodeBlock(ctx, m_name, m_argumentCount, m_isStrict, m_isConstructor, m_nativeFunctionData);
|
||||
NativeFunctionObject* result = new NativeFunctionObject(codeBlock, m_cachedObjectStructure, std::move(objectPropertyValues));
|
||||
if (m_isConstructor) {
|
||||
auto idx = m_prototypeTemplate->cachedObjectStructure()->findProperty(ctx->staticStrings().constructor).first;
|
||||
result->uncheckedGetOwnDataProperty(0).asPointerValue()->asObject()->uncheckedSetOwnDataProperty(idx, result);
|
||||
|
||||
struct Sender {
|
||||
FunctionTemplate* functionTemplate;
|
||||
Object* prototype;
|
||||
};
|
||||
|
||||
Sender d = { this, functionPrototype };
|
||||
SandBox sb(ctx);
|
||||
sb.run([](ExecutionState& state, void* data) -> Value {
|
||||
Sender* s = (Sender*)data;
|
||||
s->prototype->markAsPrototypeObject(state);
|
||||
if (s->functionTemplate->parent()) {
|
||||
s->prototype->setPrototype(state, s->functionTemplate->parent()->instantiate(state.context())->asFunctionObject()->getFunctionPrototype(state));
|
||||
}
|
||||
return Value();
|
||||
},
|
||||
&d);
|
||||
}
|
||||
|
||||
instantiatedFunctionObjects.pushBack(std::make_pair(this, result));
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
84
src/runtime/FunctionTemplate.h
Normal file
84
src/runtime/FunctionTemplate.h
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright (c) 2020-present Samsung Electronics Co., Ltd
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#ifndef __EscargotFunctionTemplate__
|
||||
#define __EscargotFunctionTemplate__
|
||||
|
||||
#include "runtime/Template.h"
|
||||
#include "runtime/ObjectTemplate.h"
|
||||
#include "parser/CodeBlock.h"
|
||||
#include "api/EscargotPublic.h"
|
||||
|
||||
namespace Escargot {
|
||||
|
||||
class FunctionTemplate : public Template {
|
||||
public:
|
||||
FunctionTemplate(AtomicString name, size_t argumentCount, bool isStrict, bool isConstructor,
|
||||
NativeFunctionPointer fn, Optional<ObjectTemplate*> instanceTemplate);
|
||||
|
||||
FunctionTemplate(AtomicString name, size_t argumentCount, bool isStrict, bool isConstructor,
|
||||
FunctionObjectRef::NativeFunctionPointer fn, Optional<ObjectTemplate*> instanceTemplate);
|
||||
|
||||
// returns the unique function instance in context.
|
||||
virtual Object* instantiate(Context* ctx) override;
|
||||
|
||||
ObjectTemplate* prototypeTemplate() const
|
||||
{
|
||||
return m_prototypeTemplate;
|
||||
}
|
||||
|
||||
// ObjectTemplate for new'ed instance of this functionTemplate
|
||||
// if there is no instance template, we will make make default object
|
||||
Optional<ObjectTemplate*> instanceTemplate() const
|
||||
{
|
||||
return m_instanceTemplate;
|
||||
}
|
||||
void setInstanceTemplate(Optional<ObjectTemplate*> s)
|
||||
{
|
||||
m_instanceTemplate = s;
|
||||
}
|
||||
|
||||
void inherit(Optional<FunctionTemplate*> parent)
|
||||
{
|
||||
ASSERT(!m_cachedObjectStructure);
|
||||
ASSERT(parent->m_isConstructor);
|
||||
m_parent = parent;
|
||||
}
|
||||
|
||||
Optional<FunctionTemplate*> parent() const
|
||||
{
|
||||
return m_parent;
|
||||
}
|
||||
|
||||
void* operator new(size_t size);
|
||||
void* operator new[](size_t size) = delete;
|
||||
|
||||
protected:
|
||||
AtomicString m_name;
|
||||
size_t m_argumentCount;
|
||||
bool m_isStrict;
|
||||
bool m_isConstructor;
|
||||
CallNativeFunctionData* m_nativeFunctionData;
|
||||
ObjectTemplate* m_prototypeTemplate;
|
||||
Optional<ObjectTemplate*> m_instanceTemplate;
|
||||
Optional<FunctionTemplate*> m_parent;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -88,6 +88,12 @@ NativeFunctionObject::NativeFunctionObject(ExecutionState& state, NativeFunction
|
|||
ASSERT(codeBlock()->hasCallNativeFunctionCode());
|
||||
}
|
||||
|
||||
NativeFunctionObject::NativeFunctionObject(CodeBlock* codeBlock, ObjectStructure* structure, ObjectPropertyValueVector&& values)
|
||||
: FunctionObject(structure, std::move(values), codeBlock->context()->globalObject()->functionPrototype())
|
||||
{
|
||||
m_codeBlock = codeBlock;
|
||||
}
|
||||
|
||||
bool NativeFunctionObject::isConstructor() const
|
||||
{
|
||||
return m_codeBlock->isNativeFunctionConstructor();
|
||||
|
|
|
|||
|
|
@ -25,6 +25,9 @@
|
|||
namespace Escargot {
|
||||
|
||||
class NativeFunctionObject : public FunctionObject {
|
||||
friend class FunctionTemplate;
|
||||
NativeFunctionObject(CodeBlock* codeBlock, ObjectStructure* structure, ObjectPropertyValueVector&& values);
|
||||
|
||||
public:
|
||||
NativeFunctionObject(ExecutionState& state, NativeFunctionInfo info);
|
||||
NativeFunctionObject(ExecutionState& state, CodeBlock* codeBlock);
|
||||
|
|
|
|||
|
|
@ -465,6 +465,15 @@ Object::Object(ExecutionState& state, size_t defaultSpace, ForGlobalBuiltin)
|
|||
m_values.resizeWithUninitializedValues(0, defaultSpace);
|
||||
}
|
||||
|
||||
Object::Object(ObjectStructure* structure, ObjectPropertyValueVector&& values, Object* proto)
|
||||
: m_structure(structure)
|
||||
, m_prototype(proto)
|
||||
, m_values(std::move(values))
|
||||
{
|
||||
ASSERT(!!structure);
|
||||
ASSERT(!!proto);
|
||||
}
|
||||
|
||||
// https://www.ecma-international.org/ecma-262/6.0/#sec-isconcatspreadable
|
||||
bool Object::isConcatSpreadable(ExecutionState& state)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -720,11 +720,11 @@ class Object : public PointerValue {
|
|||
friend class EnumerateObjectWithDestruction;
|
||||
friend class EnumerateObjectWithIteration;
|
||||
friend struct ObjectRareData;
|
||||
friend class ObjectTemplate;
|
||||
|
||||
public:
|
||||
explicit Object(ExecutionState& state);
|
||||
explicit Object(ExecutionState& state, Object* proto);
|
||||
|
||||
enum PrototypeIsNullTag { PrototypeIsNull };
|
||||
explicit Object(ExecutionState& state, PrototypeIsNullTag); // I added new function for reducing checking null for prototype
|
||||
|
||||
|
|
@ -1041,6 +1041,18 @@ public:
|
|||
Value speciesConstructor(ExecutionState& state, const Value& defaultConstructor);
|
||||
void markAsPrototypeObject(ExecutionState& state);
|
||||
|
||||
ALWAYS_INLINE Value uncheckedGetOwnDataProperty(size_t idx)
|
||||
{
|
||||
ASSERT(m_structure->readProperty(idx).m_descriptor.isDataProperty());
|
||||
return m_values[idx];
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void uncheckedSetOwnDataProperty(size_t idx, const Value& newValue)
|
||||
{
|
||||
ASSERT(m_structure->readProperty(idx).m_descriptor.isDataProperty());
|
||||
m_values[idx] = newValue;
|
||||
}
|
||||
|
||||
protected:
|
||||
static inline void fillGCDescriptor(GC_word* desc)
|
||||
{
|
||||
|
|
@ -1052,6 +1064,8 @@ protected:
|
|||
explicit Object(ExecutionState& state, Object* proto, size_t defaultSpace);
|
||||
enum ForGlobalBuiltin { __ForGlobalBuiltin__ };
|
||||
explicit Object(ExecutionState& state, size_t defaultSpace, ForGlobalBuiltin);
|
||||
// ctor for ObjectTemplate
|
||||
explicit Object(ObjectStructure* structure, ObjectPropertyValueVector&& values, Object* proto);
|
||||
|
||||
inline ObjectRareData* rareData() const
|
||||
{
|
||||
|
|
@ -1060,11 +1074,7 @@ protected:
|
|||
}
|
||||
ObjectStructure* m_structure;
|
||||
Object* m_prototype;
|
||||
#if defined(ESCARGOT_64) && defined(ESCARGOT_USE_32BIT_IN_64BIT)
|
||||
TightVectorWithNoSize<EncodedSmallValue, CustomAllocator<EncodedSmallValue>> m_values;
|
||||
#else
|
||||
TightVectorWithNoSizeUseGCRealloc<EncodedValue> m_values;
|
||||
#endif
|
||||
ObjectPropertyValueVector m_values;
|
||||
|
||||
COMPILE_ASSERT(sizeof(TightVectorWithNoSize<EncodedValue, GCUtil::gc_malloc_allocator<EncodedValue>>) == sizeof(size_t) * 1, "");
|
||||
|
||||
|
|
@ -1073,18 +1083,6 @@ protected:
|
|||
return m_structure;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Value uncheckedGetOwnDataProperty(ExecutionState& state, size_t idx)
|
||||
{
|
||||
ASSERT(m_structure->readProperty(idx).m_descriptor.isDataProperty());
|
||||
return m_values[idx];
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void uncheckedSetOwnDataProperty(ExecutionState& state, size_t idx, const Value& newValue)
|
||||
{
|
||||
ASSERT(m_structure->readProperty(idx).m_descriptor.isDataProperty());
|
||||
m_values[idx] = newValue;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Value getOwnDataPropertyUtilForObject(ExecutionState& state, size_t idx)
|
||||
{
|
||||
return getOwnDataPropertyUtilForObject(state, idx, this);
|
||||
|
|
@ -1108,7 +1106,7 @@ protected:
|
|||
return true;
|
||||
} else {
|
||||
#if defined(ESCARGOT_64) && defined(ESCARGOT_USE_32BIT_IN_64BIT)
|
||||
EncodedValue t(m_values[idx].payload());
|
||||
EncodedValue t = m_values[idx];
|
||||
bool ret = item.m_descriptor.nativeGetterSetterData()->m_setter(state, this, t, newValue);
|
||||
m_values[idx] = t;
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -265,6 +265,13 @@ public:
|
|||
m_propertyNameMap = ObjectStructureWithMap::createPropertyNameMap(newProperties);
|
||||
}
|
||||
|
||||
ObjectStructureWithMap(bool hasIndexPropertyName, ObjectStructureItemTightVector&& properties)
|
||||
: m_hasIndexPropertyName(hasIndexPropertyName)
|
||||
{
|
||||
m_properties = new ObjectStructureItemVector(std::move(properties));
|
||||
m_propertyNameMap = ObjectStructureWithMap::createPropertyNameMap(m_properties);
|
||||
}
|
||||
|
||||
virtual std::pair<size_t, Optional<const ObjectStructureItem*>> findProperty(const ObjectStructurePropertyName& s) override;
|
||||
virtual const ObjectStructureItem& readProperty(size_t idx) override;
|
||||
virtual const ObjectStructureItem* properties() const override;
|
||||
|
|
|
|||
57
src/runtime/ObjectTemplate.cpp
Normal file
57
src/runtime/ObjectTemplate.cpp
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (c) 2020-present Samsung Electronics Co., Ltd
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include "Escargot.h"
|
||||
#include "ObjectTemplate.h"
|
||||
#include "runtime/Value.h"
|
||||
#include "runtime/Object.h"
|
||||
#include "runtime/Context.h"
|
||||
#include "runtime/FunctionTemplate.h"
|
||||
|
||||
namespace Escargot {
|
||||
|
||||
void* ObjectTemplate::operator new(size_t size)
|
||||
{
|
||||
static bool typeInited = false;
|
||||
static GC_descr descr;
|
||||
if (!typeInited) {
|
||||
GC_word objBitmap[GC_BITMAP_SIZE(ObjectTemplate)] = { 0 };
|
||||
ObjectTemplate::fillGCDescriptor(objBitmap);
|
||||
descr = GC_make_descriptor(objBitmap, GC_WORD_LEN(ObjectTemplate));
|
||||
typeInited = true;
|
||||
}
|
||||
return GC_MALLOC_EXPLICITLY_TYPED(size, descr);
|
||||
}
|
||||
|
||||
ObjectTemplate::ObjectTemplate()
|
||||
{
|
||||
}
|
||||
|
||||
Object* ObjectTemplate::instantiate(Context* ctx)
|
||||
{
|
||||
size_t propertyCount = m_properties.size();
|
||||
if (!m_cachedObjectStructure) {
|
||||
m_cachedObjectStructure = constructObjectStructure(ctx, nullptr, 0);
|
||||
}
|
||||
ObjectPropertyValueVector objectPropertyValues;
|
||||
constructObjectPropertyValues(ctx, nullptr, 0, objectPropertyValues);
|
||||
|
||||
return new Object(m_cachedObjectStructure, std::move(objectPropertyValues), ctx->globalObject()->objectPrototype());
|
||||
}
|
||||
}
|
||||
40
src/runtime/ObjectTemplate.h
Normal file
40
src/runtime/ObjectTemplate.h
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (c) 2020-present Samsung Electronics Co., Ltd
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#ifndef __EscargotObjectTemplate__
|
||||
#define __EscargotObjectTemplate__
|
||||
|
||||
#include "runtime/Template.h"
|
||||
|
||||
namespace Escargot {
|
||||
|
||||
class ObjectTemplate : public Template {
|
||||
public:
|
||||
ObjectTemplate();
|
||||
|
||||
virtual Object* instantiate(Context* ctx);
|
||||
|
||||
void* operator new(size_t size);
|
||||
void* operator new[](size_t size) = delete;
|
||||
|
||||
protected:
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
146
src/runtime/Template.cpp
Normal file
146
src/runtime/Template.cpp
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* Copyright (c) 2020-present Samsung Electronics Co., Ltd
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include "Escargot.h"
|
||||
#include "Template.h"
|
||||
#include "runtime/FunctionTemplate.h"
|
||||
|
||||
namespace Escargot {
|
||||
|
||||
ObjectStructurePropertyName TemplatePropertyName::toObjectStructurePropertyName(Context* c)
|
||||
{
|
||||
if (m_ptr->isString()) {
|
||||
return ObjectStructurePropertyName(AtomicString(c, m_ptr->asString()));
|
||||
} else {
|
||||
return ObjectStructurePropertyName(m_ptr->asSymbol());
|
||||
}
|
||||
}
|
||||
|
||||
bool Template::has(const TemplatePropertyName& name)
|
||||
{
|
||||
for (size_t i = 0; i < m_properties.size(); i++) {
|
||||
if (m_properties[i].first == name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Template::remove(const TemplatePropertyName& name)
|
||||
{
|
||||
ASSERT(m_cachedObjectStructure == nullptr);
|
||||
for (size_t i = 0; i < m_properties.size(); i++) {
|
||||
if (m_properties[i].first == name) {
|
||||
m_properties.erase(i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Template::set(const TemplatePropertyName& name, const TemplateData& data, bool isWritable, bool isEnumerable, bool isConfigurable)
|
||||
{
|
||||
ASSERT(m_cachedObjectStructure == nullptr);
|
||||
remove(name);
|
||||
m_properties.pushBack(std::make_pair(name, TemplatePropertyData(data, isWritable, isEnumerable, isConfigurable)));
|
||||
}
|
||||
|
||||
void Template::setAccessorProperty(const TemplatePropertyName& name, Optional<FunctionTemplate*> getter, Optional<FunctionTemplate*> setter, bool isEnumerable, bool isConfigurable)
|
||||
{
|
||||
ASSERT(m_cachedObjectStructure == nullptr);
|
||||
remove(name);
|
||||
m_properties.pushBack(std::make_pair(name, TemplatePropertyData(getter, setter, isEnumerable, isConfigurable)));
|
||||
}
|
||||
|
||||
void Template::setNativeDataAccessorProperty(const TemplatePropertyName& name, ObjectPropertyNativeGetterSetterData* nativeGetterSetterData, void* privateData)
|
||||
{
|
||||
ASSERT(m_cachedObjectStructure == nullptr);
|
||||
remove(name);
|
||||
m_properties.pushBack(std::make_pair(name, TemplatePropertyData(nativeGetterSetterData, privateData)));
|
||||
}
|
||||
|
||||
ObjectStructure* Template::constructObjectStructure(Context* ctx, ObjectStructureItem* baseItems, size_t baseItemCount)
|
||||
{
|
||||
size_t propertyCount = m_properties.size() + baseItemCount;
|
||||
ObjectStructureItemTightVector structureItemVector;
|
||||
structureItemVector.resizeWithUninitializedValues(propertyCount);
|
||||
|
||||
for (size_t i = 0; i < baseItemCount; i++) {
|
||||
structureItemVector[i] = baseItems[i];
|
||||
}
|
||||
|
||||
bool hasIndexStringAsPropertyName = false;
|
||||
bool hasNonAtomicPropertyName = false;
|
||||
for (size_t i = baseItemCount; i < propertyCount; i++) {
|
||||
auto propertyName = m_properties[i].first.toObjectStructurePropertyName(ctx);
|
||||
if (!hasIndexStringAsPropertyName) {
|
||||
hasIndexStringAsPropertyName |= propertyName.isIndexString();
|
||||
}
|
||||
|
||||
hasNonAtomicPropertyName |= !propertyName.hasAtomicString();
|
||||
|
||||
auto type = m_properties[i].second.propertyType();
|
||||
ObjectStructurePropertyDescriptor desc;
|
||||
if (type == Template::TemplatePropertyData::PropertyType::PropertyValueData || type == Template::TemplatePropertyData::PropertyType::PropertyTemplateData) {
|
||||
desc = ObjectStructurePropertyDescriptor::createDataDescriptor(m_properties[i].second.presentAttributes());
|
||||
} else if (type == Template::TemplatePropertyData::PropertyType::PropertyNativeAccessorData) {
|
||||
desc = ObjectStructurePropertyDescriptor::createDataButHasNativeGetterSetterDescriptor(m_properties[i].second.nativeAccessorData());
|
||||
} else {
|
||||
ASSERT(type == Template::TemplatePropertyData::PropertyType::PropertyAccessorData);
|
||||
desc = ObjectStructurePropertyDescriptor::createAccessorDescriptor(m_properties[i].second.presentAttributes());
|
||||
}
|
||||
structureItemVector[i] = ObjectStructureItem(propertyName, desc);
|
||||
}
|
||||
|
||||
ObjectStructure* newObjectStructure;
|
||||
if (propertyCount > ESCARGOT_OBJECT_STRUCTURE_ACCESS_CACHE_BUILD_MIN_SIZE) {
|
||||
newObjectStructure = new ObjectStructureWithMap(hasIndexStringAsPropertyName, std::move(structureItemVector));
|
||||
} else {
|
||||
newObjectStructure = new ObjectStructureWithTransition(std::move(structureItemVector), hasIndexStringAsPropertyName, hasNonAtomicPropertyName);
|
||||
}
|
||||
|
||||
return newObjectStructure;
|
||||
}
|
||||
|
||||
void Template::constructObjectPropertyValues(Context* ctx, ObjectPropertyValue* baseItems, size_t baseItemCount, ObjectPropertyValueVector& objectPropertyValues)
|
||||
{
|
||||
size_t propertyCount = m_properties.size() + baseItemCount;
|
||||
objectPropertyValues.resizeWithUninitializedValues(0, propertyCount);
|
||||
|
||||
for (size_t i = 0; i < baseItemCount; i++) {
|
||||
objectPropertyValues[i] = baseItems[i];
|
||||
}
|
||||
|
||||
for (size_t i = baseItemCount; i < propertyCount; i++) {
|
||||
auto type = m_properties[i].second.propertyType();
|
||||
if (type == Template::TemplatePropertyData::PropertyType::PropertyValueData) {
|
||||
objectPropertyValues[i] = m_properties[i].second.valueData();
|
||||
} else if (type == Template::TemplatePropertyData::PropertyType::PropertyTemplateData) {
|
||||
objectPropertyValues[i] = m_properties[i].second.templateData()->instantiate(ctx);
|
||||
} else if (type == Template::TemplatePropertyData::PropertyType::PropertyNativeAccessorData) {
|
||||
objectPropertyValues[i] = Value(Value::FromPayload, (intptr_t)m_properties[i].second.nativeAccessorPrivateData());
|
||||
} else {
|
||||
ASSERT(type == Template::TemplatePropertyData::PropertyType::PropertyAccessorData);
|
||||
Value getter = m_properties[i].second.accessorData().m_getterTemplate ? m_properties[i].second.accessorData().m_getterTemplate->instantiate(ctx) : Value(Value::EmptyValue);
|
||||
Value setter = m_properties[i].second.accessorData().m_setterTemplate ? m_properties[i].second.accessorData().m_setterTemplate->instantiate(ctx) : Value(Value::EmptyValue);
|
||||
objectPropertyValues[i] = new JSGetterSetter(getter, setter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
298
src/runtime/Template.h
Normal file
298
src/runtime/Template.h
Normal file
|
|
@ -0,0 +1,298 @@
|
|||
/*
|
||||
* Copyright (c) 2020-present Samsung Electronics Co., Ltd
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#ifndef __EscargotTemplate__
|
||||
#define __EscargotTemplate__
|
||||
|
||||
#include "util/Vector.h"
|
||||
#include "runtime/Value.h"
|
||||
#include "runtime/ObjectStructure.h"
|
||||
|
||||
namespace Escargot {
|
||||
|
||||
class Template;
|
||||
class ObjectTemplate;
|
||||
class FunctionTemplate;
|
||||
|
||||
class TemplatePropertyName {
|
||||
public:
|
||||
TemplatePropertyName(const AtomicString& s)
|
||||
: m_ptr(s.string())
|
||||
{
|
||||
}
|
||||
|
||||
TemplatePropertyName(String* s)
|
||||
: m_ptr(s)
|
||||
{
|
||||
}
|
||||
|
||||
TemplatePropertyName(Symbol* s)
|
||||
: m_ptr(s)
|
||||
{
|
||||
}
|
||||
|
||||
TemplatePropertyName(PointerValue* s)
|
||||
: m_ptr(s)
|
||||
{
|
||||
ASSERT(s->isString() || s->isSymbol());
|
||||
}
|
||||
|
||||
TemplatePropertyName(const TemplatePropertyName& src)
|
||||
: m_ptr(src.m_ptr)
|
||||
{
|
||||
}
|
||||
|
||||
ObjectStructurePropertyName toObjectStructurePropertyName(Context* c);
|
||||
|
||||
bool equals(const TemplatePropertyName& src)
|
||||
{
|
||||
// fast path
|
||||
if (m_ptr == src.m_ptr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m_ptr->isSymbol() || src.m_ptr->isSymbol()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_ptr->asString()->equals(src.m_ptr->asString());
|
||||
}
|
||||
|
||||
bool operator==(const TemplatePropertyName& src)
|
||||
{
|
||||
return equals(src);
|
||||
}
|
||||
|
||||
bool operator!=(const TemplatePropertyName& src)
|
||||
{
|
||||
return !equals(src);
|
||||
}
|
||||
|
||||
private:
|
||||
PointerValue* m_ptr;
|
||||
};
|
||||
|
||||
class TemplateData : public gc {
|
||||
friend class Template;
|
||||
|
||||
public:
|
||||
enum TemplateDataType {
|
||||
TemplateDataValue,
|
||||
TemplateDataTemplate,
|
||||
};
|
||||
|
||||
TemplateData(const Value& v)
|
||||
{
|
||||
m_type = TemplateDataValue;
|
||||
m_value = v;
|
||||
}
|
||||
|
||||
TemplateData(Template* t)
|
||||
{
|
||||
m_type = TemplateDataTemplate;
|
||||
m_template = t;
|
||||
}
|
||||
|
||||
private:
|
||||
TemplateDataType m_type;
|
||||
union {
|
||||
EncodedValue m_value;
|
||||
Template* m_template;
|
||||
};
|
||||
};
|
||||
|
||||
// don't modify template after create object
|
||||
// it is not intented operation
|
||||
class Template : public gc {
|
||||
public:
|
||||
void set(const TemplatePropertyName& name, const TemplateData& data, bool isWritable, bool isEnumerable, bool isConfigurable);
|
||||
void setAccessorProperty(const TemplatePropertyName& name, Optional<FunctionTemplate*> getter, Optional<FunctionTemplate*> setter, bool isEnumerable, bool isConfigurable);
|
||||
void setNativeDataAccessorProperty(const TemplatePropertyName& name, ObjectPropertyNativeGetterSetterData* nativeGetterSetterData, void* privateData);
|
||||
|
||||
bool has(const TemplatePropertyName& name);
|
||||
// return true if removed
|
||||
bool remove(const TemplatePropertyName& name);
|
||||
|
||||
virtual Object* instantiate(Context* ctx) = 0;
|
||||
|
||||
bool didInstantiate() const
|
||||
{
|
||||
return m_cachedObjectStructure;
|
||||
}
|
||||
|
||||
Optional<ObjectStructure*> cachedObjectStructure()
|
||||
{
|
||||
return m_cachedObjectStructure;
|
||||
}
|
||||
|
||||
protected:
|
||||
ObjectStructure* constructObjectStructure(Context* ctx, ObjectStructureItem* baseItems, size_t baseItemCount);
|
||||
void constructObjectPropertyValues(Context* ctx, ObjectPropertyValue* baseItems, size_t baseItemCount, ObjectPropertyValueVector& objectPropertyValues);
|
||||
|
||||
Template()
|
||||
: m_cachedObjectStructure(nullptr)
|
||||
{
|
||||
}
|
||||
virtual ~Template()
|
||||
{
|
||||
}
|
||||
|
||||
static inline void fillGCDescriptor(GC_word* desc)
|
||||
{
|
||||
GC_set_bit(desc, GC_WORD_OFFSET(Template, m_properties));
|
||||
GC_set_bit(desc, GC_WORD_OFFSET(Template, m_cachedObjectStructure));
|
||||
}
|
||||
|
||||
class TemplatePropertyData {
|
||||
public:
|
||||
enum PropertyType {
|
||||
PropertyValueData,
|
||||
PropertyTemplateData,
|
||||
PropertyAccessorData,
|
||||
PropertyNativeAccessorData,
|
||||
};
|
||||
|
||||
struct AccessorData {
|
||||
Optional<FunctionTemplate*> m_getterTemplate;
|
||||
Optional<FunctionTemplate*> m_setterTemplate;
|
||||
};
|
||||
|
||||
TemplatePropertyData(const TemplateData& data, bool isWritable, bool isEnumerable, bool isConfigurable)
|
||||
{
|
||||
if (data.m_type == TemplateData::TemplateDataValue) {
|
||||
m_value = data.m_value;
|
||||
m_propertyData = 1;
|
||||
m_propertyData = m_propertyData | (PropertyValueData << 1);
|
||||
} else {
|
||||
m_template = data.m_template;
|
||||
m_propertyData = 1;
|
||||
m_propertyData = m_propertyData | (PropertyTemplateData << 1);
|
||||
}
|
||||
|
||||
int attr = 0;
|
||||
|
||||
if (isWritable) {
|
||||
attr |= ObjectStructurePropertyDescriptor::PresentAttribute::WritablePresent;
|
||||
}
|
||||
if (isEnumerable) {
|
||||
attr |= ObjectStructurePropertyDescriptor::PresentAttribute::EnumerablePresent;
|
||||
}
|
||||
if (isConfigurable) {
|
||||
attr |= ObjectStructurePropertyDescriptor::PresentAttribute::ConfigurablePresent;
|
||||
}
|
||||
|
||||
m_propertyData = m_propertyData | (attr << 4);
|
||||
}
|
||||
|
||||
TemplatePropertyData(Optional<FunctionTemplate*> getter, Optional<FunctionTemplate*> setter, bool isEnumerable, bool isConfigurable)
|
||||
{
|
||||
m_accessorData.m_getterTemplate = getter;
|
||||
m_accessorData.m_setterTemplate = setter;
|
||||
|
||||
m_propertyData = 1;
|
||||
m_propertyData = m_propertyData | (PropertyAccessorData << 1);
|
||||
|
||||
int attr = 0;
|
||||
if (isEnumerable) {
|
||||
attr |= ObjectStructurePropertyDescriptor::PresentAttribute::EnumerablePresent;
|
||||
}
|
||||
if (isConfigurable) {
|
||||
attr |= ObjectStructurePropertyDescriptor::PresentAttribute::ConfigurablePresent;
|
||||
}
|
||||
|
||||
m_propertyData = m_propertyData | (attr << 4);
|
||||
}
|
||||
|
||||
TemplatePropertyData(ObjectPropertyNativeGetterSetterData* nativeGetterSetterData, void* privateData)
|
||||
{
|
||||
m_nativeAccessorData.m_nativeGetterSetterData = nativeGetterSetterData;
|
||||
m_nativeAccessorData.m_privateData = privateData;
|
||||
|
||||
m_propertyData = 1;
|
||||
m_propertyData = m_propertyData | (PropertyNativeAccessorData << 1);
|
||||
m_propertyData = m_propertyData | (nativeGetterSetterData->m_presentAttributes << 4);
|
||||
}
|
||||
|
||||
TemplatePropertyData(const TemplatePropertyData& src)
|
||||
{
|
||||
m_propertyData = src.m_propertyData;
|
||||
m_accessorData = src.m_accessorData;
|
||||
}
|
||||
|
||||
PropertyType propertyType() const
|
||||
{
|
||||
return (PropertyType)((m_propertyData >> 1) & 0x3);
|
||||
}
|
||||
|
||||
ObjectStructurePropertyDescriptor::PresentAttribute presentAttributes() const
|
||||
{
|
||||
return (ObjectStructurePropertyDescriptor::PresentAttribute)(m_propertyData >> 4);
|
||||
}
|
||||
|
||||
Value valueData() const
|
||||
{
|
||||
ASSERT(propertyType() == PropertyType::PropertyValueData);
|
||||
return m_value;
|
||||
}
|
||||
|
||||
Template* templateData() const
|
||||
{
|
||||
ASSERT(propertyType() == PropertyType::PropertyTemplateData);
|
||||
return m_template;
|
||||
}
|
||||
|
||||
AccessorData accessorData() const
|
||||
{
|
||||
ASSERT(propertyType() == PropertyType::PropertyAccessorData);
|
||||
return m_accessorData;
|
||||
}
|
||||
|
||||
ObjectPropertyNativeGetterSetterData* nativeAccessorData() const
|
||||
{
|
||||
ASSERT(propertyType() == PropertyType::PropertyNativeAccessorData);
|
||||
return m_nativeAccessorData.m_nativeGetterSetterData;
|
||||
}
|
||||
|
||||
void* nativeAccessorPrivateData() const
|
||||
{
|
||||
ASSERT(propertyType() == PropertyType::PropertyNativeAccessorData);
|
||||
return m_nativeAccessorData.m_privateData;
|
||||
}
|
||||
|
||||
protected:
|
||||
size_t m_propertyData;
|
||||
|
||||
union {
|
||||
EncodedValue m_value;
|
||||
Template* m_template;
|
||||
AccessorData m_accessorData;
|
||||
struct {
|
||||
ObjectPropertyNativeGetterSetterData* m_nativeGetterSetterData;
|
||||
void* m_privateData;
|
||||
} m_nativeAccessorData;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(TemplatePropertyData) == sizeof(size_t) * 3, "");
|
||||
|
||||
Vector<std::pair<TemplatePropertyName, TemplatePropertyData>, GCUtil::gc_malloc_allocator<std::pair<TemplatePropertyName, TemplatePropertyData>>> m_properties;
|
||||
ObjectStructure* m_cachedObjectStructure;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,185 +1,525 @@
|
|||
/*
|
||||
* Copyright (c) 2017-present Samsung Electronics Co., Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include <EscargotPublic.h>
|
||||
#include <string.h>
|
||||
#include "api/EscargotPublic.h"
|
||||
|
||||
#define CHECK(name, cond) \
|
||||
printf(name" | %s\n", (cond) ? "pass" : "fail");
|
||||
using namespace Escargot;
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
static bool stringEndsWith(const std::string& str, const std::string& suffix)
|
||||
{
|
||||
return str.size() >= suffix.size() && 0 == str.compare(str.size() - suffix.size(), suffix.size(), suffix);
|
||||
}
|
||||
|
||||
static const char32_t offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, 0x03C82080UL, static_cast<char32_t>(0xFA082080UL), static_cast<char32_t>(0x82082080UL) };
|
||||
|
||||
char32_t readUTF8Sequence(const char*& sequence, bool& valid, int& charlen)
|
||||
{
|
||||
unsigned length;
|
||||
const char sch = *sequence;
|
||||
valid = true;
|
||||
if ((sch & 0x80) == 0)
|
||||
length = 1;
|
||||
else {
|
||||
unsigned char ch2 = static_cast<unsigned char>(*(sequence + 1));
|
||||
if ((sch & 0xE0) == 0xC0
|
||||
&& (ch2 & 0xC0) == 0x80)
|
||||
length = 2;
|
||||
else {
|
||||
unsigned char ch3 = static_cast<unsigned char>(*(sequence + 2));
|
||||
if ((sch & 0xF0) == 0xE0
|
||||
&& (ch2 & 0xC0) == 0x80
|
||||
&& (ch3 & 0xC0) == 0x80)
|
||||
length = 3;
|
||||
else {
|
||||
unsigned char ch4 = static_cast<unsigned char>(*(sequence + 3));
|
||||
if ((sch & 0xF8) == 0xF0
|
||||
&& (ch2 & 0xC0) == 0x80
|
||||
&& (ch3 & 0xC0) == 0x80
|
||||
&& (ch4 & 0xC0) == 0x80)
|
||||
length = 4;
|
||||
else {
|
||||
valid = false;
|
||||
sequence++;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
charlen = length;
|
||||
char32_t ch = 0;
|
||||
switch (length) {
|
||||
case 4:
|
||||
ch += static_cast<unsigned char>(*sequence++);
|
||||
ch <<= 6; // Fall through.
|
||||
case 3:
|
||||
ch += static_cast<unsigned char>(*sequence++);
|
||||
ch <<= 6; // Fall through.
|
||||
case 2:
|
||||
ch += static_cast<unsigned char>(*sequence++);
|
||||
ch <<= 6; // Fall through.
|
||||
case 1:
|
||||
ch += static_cast<unsigned char>(*sequence++);
|
||||
}
|
||||
return ch - offsetsFromUTF8[length - 1];
|
||||
}
|
||||
|
||||
static std::string evalScript(ContextRef* context, StringRef* str, StringRef* fileName, bool isModule)
|
||||
{
|
||||
if (stringEndsWith(fileName->toStdUTF8String(), "mjs")) {
|
||||
isModule = isModule || true;
|
||||
}
|
||||
|
||||
auto scriptInitializeResult = context->scriptParser()->initializeScript(str, fileName, isModule);
|
||||
if (!scriptInitializeResult.script) {
|
||||
std::string result("Script parsing error: ");
|
||||
switch (scriptInitializeResult.parseErrorCode) {
|
||||
case Escargot::ErrorObjectRef::Code::SyntaxError:
|
||||
result += "SyntaxError";
|
||||
break;
|
||||
case Escargot::ErrorObjectRef::Code::EvalError:
|
||||
result += "EvalError";
|
||||
break;
|
||||
case Escargot::ErrorObjectRef::Code::RangeError:
|
||||
result += "RangeError";
|
||||
break;
|
||||
case Escargot::ErrorObjectRef::Code::ReferenceError:
|
||||
result += "ReferenceError";
|
||||
break;
|
||||
case Escargot::ErrorObjectRef::Code::TypeError:
|
||||
result += "TypeError";
|
||||
break;
|
||||
case Escargot::ErrorObjectRef::Code::URIError:
|
||||
result += "URIError";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
char str[256];
|
||||
snprintf(str, sizeof(str), ": %s\n", scriptInitializeResult.parseErrorMessage->toStdUTF8String().data());
|
||||
result += str;
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string result;
|
||||
auto evalResult = Evaluator::execute(context, [](ExecutionStateRef* state, ScriptRef* script) -> ValueRef* {
|
||||
return script->execute(state);
|
||||
},
|
||||
scriptInitializeResult.script.get());
|
||||
|
||||
char str[256];
|
||||
if (!evalResult.isSuccessful()) {
|
||||
snprintf(str, sizeof(str), "Uncaught %s:\n", evalResult.resultOrErrorToString(context)->toStdUTF8String().data());
|
||||
result += str;
|
||||
for (size_t i = 0; i < evalResult.stackTraceData.size(); i++) {
|
||||
snprintf(str, sizeof(str), "%s (%d:%d)\n", evalResult.stackTraceData[i].src->toStdUTF8String().data(), (int)evalResult.stackTraceData[i].loc.line, (int)evalResult.stackTraceData[i].loc.column);
|
||||
result += str;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
result += evalResult.resultOrErrorToString(context)->toStdUTF8String();
|
||||
|
||||
while (context->vmInstance()->hasPendingPromiseJob()) {
|
||||
context->vmInstance()->executePendingPromiseJob();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static OptionalRef<StringRef> builtinHelperFileRead(OptionalRef<ExecutionStateRef> state, const char* fileName, const char* builtinName)
|
||||
{
|
||||
FILE* fp = fopen(fileName, "r");
|
||||
if (fp) {
|
||||
StringRef* src = StringRef::emptyString();
|
||||
std::string utf8Str;
|
||||
std::basic_string<unsigned char, std::char_traits<unsigned char>> str;
|
||||
char buf[512];
|
||||
bool hasNonLatin1Content = false;
|
||||
size_t readLen;
|
||||
while ((readLen = fread(buf, 1, sizeof buf, fp))) {
|
||||
if (!hasNonLatin1Content) {
|
||||
const char* source = buf;
|
||||
int charlen;
|
||||
bool valid;
|
||||
while (source < buf + readLen) {
|
||||
char32_t ch = readUTF8Sequence(source, valid, charlen);
|
||||
if (ch > 255) {
|
||||
hasNonLatin1Content = true;
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
break;
|
||||
} else {
|
||||
str += (unsigned char)ch;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
utf8Str.append(buf, readLen);
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
if (StringRef::isCompressibleStringEnabled()) {
|
||||
if (state) {
|
||||
if (hasNonLatin1Content) {
|
||||
src = StringRef::createFromUTF8ToCompressibleString(state->context(), utf8Str.data(), utf8Str.length());
|
||||
} else {
|
||||
src = StringRef::createFromLatin1ToCompressibleString(state->context(), str.data(), str.length());
|
||||
}
|
||||
} else {
|
||||
if (hasNonLatin1Content) {
|
||||
src = StringRef::createFromUTF8(utf8Str.data(), utf8Str.length());
|
||||
} else {
|
||||
src = StringRef::createFromLatin1(str.data(), str.length());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (hasNonLatin1Content) {
|
||||
src = StringRef::createFromUTF8(utf8Str.data(), utf8Str.length());
|
||||
} else {
|
||||
src = StringRef::createFromLatin1(str.data(), str.length());
|
||||
}
|
||||
}
|
||||
return src;
|
||||
} else {
|
||||
char msg[1024];
|
||||
snprintf(msg, sizeof(msg), "GlobalObject.%s: cannot open file %s", builtinName, fileName);
|
||||
if (state) {
|
||||
state->throwException(URIErrorObjectRef::create(state.get(), StringRef::createFromUTF8(msg, strnlen(msg, sizeof msg))));
|
||||
} else {
|
||||
puts(msg);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
class ShellPlatform : public PlatformRef {
|
||||
public:
|
||||
virtual void didPromiseJobEnqueued(ContextRef* relatedContext, PromiseObjectRef* obj) override
|
||||
{
|
||||
// ignore. we always check pending job after eval script
|
||||
}
|
||||
|
||||
static std::string dirnameOf(const std::string& fname)
|
||||
{
|
||||
size_t pos = fname.find_last_of("/");
|
||||
if (std::string::npos == pos) {
|
||||
pos = fname.find_last_of("\\/");
|
||||
}
|
||||
return (std::string::npos == pos)
|
||||
? ""
|
||||
: fname.substr(0, pos);
|
||||
}
|
||||
|
||||
static std::string absolutePath(const std::string& referrerPath, const std::string& src)
|
||||
{
|
||||
std::string utf8MayRelativePath = dirnameOf(referrerPath) + "/" + src;
|
||||
auto absPath = realpath(utf8MayRelativePath.data(), nullptr);
|
||||
if (!absPath) {
|
||||
return std::string();
|
||||
}
|
||||
std::string utf8AbsolutePath = absPath ? absPath : "";
|
||||
free(absPath);
|
||||
|
||||
return utf8AbsolutePath;
|
||||
}
|
||||
|
||||
static std::string absolutePath(const std::string& src)
|
||||
{
|
||||
auto absPath = realpath(src.data(), nullptr);
|
||||
std::string utf8AbsolutePath = absPath;
|
||||
free(absPath);
|
||||
|
||||
return utf8AbsolutePath;
|
||||
}
|
||||
|
||||
std::vector<std::tuple<std::string /* abs path */, ContextRef*, PersistentRefHolder<ScriptRef>>> loadedModules;
|
||||
virtual LoadModuleResult onLoadModule(ContextRef* relatedContext, ScriptRef* whereRequestFrom, StringRef* moduleSrc) override
|
||||
{
|
||||
std::string referrerPath = whereRequestFrom->src()->toStdUTF8String();
|
||||
|
||||
for (size_t i = 0; i < loadedModules.size(); i++) {
|
||||
if (std::get<2>(loadedModules[i]) == whereRequestFrom) {
|
||||
referrerPath = std::get<0>(loadedModules[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::string absPath = absolutePath(referrerPath, moduleSrc->toStdUTF8String());
|
||||
if (absPath.length() == 0) {
|
||||
std::string s = "Error reading : " + moduleSrc->toStdUTF8String();
|
||||
return LoadModuleResult(ErrorObjectRef::Code::None, StringRef::createFromUTF8(s.data(), s.length()));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < loadedModules.size(); i++) {
|
||||
if (std::get<0>(loadedModules[i]) == absPath && std::get<1>(loadedModules[i]) == relatedContext) {
|
||||
return LoadModuleResult(std::get<2>(loadedModules[i]));
|
||||
}
|
||||
}
|
||||
|
||||
OptionalRef<StringRef> source = builtinHelperFileRead(nullptr, absPath.data(), "");
|
||||
if (!source) {
|
||||
std::string s = "Error reading : " + absPath;
|
||||
return LoadModuleResult(ErrorObjectRef::Code::None, StringRef::createFromUTF8(s.data(), s.length()));
|
||||
}
|
||||
|
||||
auto parseResult = relatedContext->scriptParser()->initializeScript(source.value(), StringRef::createFromUTF8(absPath.data(), absPath.size()), true);
|
||||
if (!parseResult.isSuccessful()) {
|
||||
return LoadModuleResult(parseResult.parseErrorCode, parseResult.parseErrorMessage);
|
||||
}
|
||||
|
||||
return LoadModuleResult(parseResult.script.get());
|
||||
}
|
||||
|
||||
virtual void didLoadModule(ContextRef* relatedContext, OptionalRef<ScriptRef> referrer, ScriptRef* loadedModule) override
|
||||
{
|
||||
std::string path;
|
||||
if (referrer && loadedModule->src()->length() && loadedModule->src()->charAt(0) != '/') {
|
||||
path = absolutePath(referrer->src()->toStdUTF8String(), loadedModule->src()->toStdUTF8String());
|
||||
} else {
|
||||
path = absolutePath(loadedModule->src()->toStdUTF8String());
|
||||
}
|
||||
loadedModules.push_back(std::make_tuple(path, relatedContext, PersistentRefHolder<ScriptRef>(loadedModule)));
|
||||
}
|
||||
};
|
||||
|
||||
PersistentRefHolder<VMInstanceRef> g_instance;
|
||||
PersistentRefHolder<ContextRef> g_context;
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
setbuf(stdout, NULL);
|
||||
setbuf(stderr, NULL);
|
||||
#endif
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
printf("testapi begins\n");
|
||||
Globals::initialize();
|
||||
|
||||
Escargot::Globals::initialize();
|
||||
Escargot::VMInstanceRef* vm = Escargot::VMInstanceRef::create();
|
||||
Escargot::ContextRef* ctx = Escargot::ContextRef::create(vm);
|
||||
Escargot::ObjectRef* globalObject = ctx->globalObject();
|
||||
Memory::setGCFrequency(24);
|
||||
|
||||
const char* script = "4+3";
|
||||
const char* filename = "FileName.js";
|
||||
printf("evaluateScript %s=", script);
|
||||
|
||||
Escargot::ScriptRef* scriptRef = ctx->scriptParser()->parse(Escargot::StringRef::fromASCII(script, strlen(script)), Escargot::StringRef::fromASCII(filename, strlen(filename))).m_script;
|
||||
Escargot::SandBoxRef* sb = Escargot::SandBoxRef::create(ctx);
|
||||
auto sandBoxResult = sb->run([&](Escargot::ExecutionStateRef* state) -> Escargot::ValueRef* {
|
||||
return scriptRef->execute(state);
|
||||
ShellPlatform* platform = new ShellPlatform();
|
||||
PersistentRefHolder<VMInstanceRef> g_instance = VMInstanceRef::create(platform);
|
||||
g_instance->setOnVMInstanceDelete([](VMInstanceRef* instance) {
|
||||
delete instance->platform();
|
||||
});
|
||||
|
||||
Escargot::ValueRef* evalResult = sandBoxResult.result;
|
||||
sb->destroy();
|
||||
g_context = ContextRef::create(g_instance.get());
|
||||
|
||||
Escargot::ExecutionStateRef* es = Escargot::ExecutionStateRef::create(ctx);
|
||||
puts(evalResult->toString(es)->toStdUTF8String().c_str());
|
||||
|
||||
Escargot::FunctionObjectRef::NativeFunctionInfo printInfo(Escargot::AtomicStringRef::create(ctx, "print"), [](Escargot::ExecutionStateRef* state, Escargot::ValueRef* thisValue, size_t argc, Escargot::ValueRef** argv, bool isNewExpression) -> Escargot::ValueRef* {
|
||||
puts(argv[0]->toString(state)->toStdUTF8String().data());
|
||||
return Escargot::ValueRef::createUndefined();
|
||||
}, 1, nullptr, true, false);
|
||||
|
||||
Escargot::FunctionObjectRef* printFn = Escargot::FunctionObjectRef::create(es, printInfo);
|
||||
globalObject->set(es, Escargot::ValueRef::create(Escargot::StringRef::fromASCII("print")), Escargot::ValueRef::create(printFn));
|
||||
|
||||
Escargot::ValueRef* jsbool = Escargot::ValueRef::create(true);
|
||||
Escargot::ValueRef* jsnumber = Escargot::ValueRef::create(123);
|
||||
Escargot::ValueRef* jsundefined = Escargot::ValueRef::createUndefined();
|
||||
Escargot::ValueRef* jsnull = Escargot::ValueRef::createNull();
|
||||
Escargot::ValueRef* jsobject = Escargot::ValueRef::create(Escargot::ObjectRef::create(es));
|
||||
|
||||
Escargot::ValueRef* jstest = jsbool;
|
||||
CHECK("ValueRef type check 1", jstest->isBoolean());
|
||||
CHECK("ValueRef type check 2", !jstest->isNumber());
|
||||
CHECK("ValueRef type check 3", !jstest->isNull());
|
||||
CHECK("ValueRef type check 4", !jstest->isUndefined());
|
||||
CHECK("ValueRef type check 5", !jstest->isObject());
|
||||
|
||||
jstest = jsnumber;
|
||||
CHECK("ValueRef type check 6", !jstest->isBoolean());
|
||||
CHECK("ValueRef type check 7", jstest->isNumber());
|
||||
CHECK("ValueRef type check 8", !jstest->isNull());
|
||||
CHECK("ValueRef type check 9", !jstest->isUndefined());
|
||||
CHECK("ValueRef type check 10", !jstest->isObject());
|
||||
|
||||
jstest = jsnull;
|
||||
CHECK("ValueRef type check 11", !jstest->isBoolean());
|
||||
CHECK("ValueRef type check 12", !jstest->isNumber());
|
||||
CHECK("ValueRef type check 13", jstest->isNull());
|
||||
CHECK("ValueRef type check 14", !jstest->isUndefined());
|
||||
CHECK("ValueRef type check 15", !jstest->isObject());
|
||||
|
||||
jstest = jsundefined;
|
||||
CHECK("ValueRef type check 16", !jstest->isBoolean());
|
||||
CHECK("ValueRef type check 17", !jstest->isNumber());
|
||||
CHECK("ValueRef type check 18", !jstest->isNull());
|
||||
CHECK("ValueRef type check 19", jstest->isUndefined());
|
||||
CHECK("ValueRef type check 20", !jstest->isObject());
|
||||
|
||||
jstest = jsobject;
|
||||
CHECK("ValueRef type check 21", !jstest->isBoolean());
|
||||
CHECK("ValueRef type check 22", !jstest->isNumber());
|
||||
CHECK("ValueRef type check 23", !jstest->isNull());
|
||||
CHECK("ValueRef type check 24", !jstest->isUndefined());
|
||||
CHECK("ValueRef type check 25", jstest->isObject());
|
||||
|
||||
CHECK("ValueRef type conversion 1", jsbool->toBoolean(es));
|
||||
CHECK("ValueRef type conversion 2", jsnumber->toNumber(es) == 123);
|
||||
|
||||
CHECK("globalObject() is not null", !Escargot::ValueRef::create(globalObject)->isNull());
|
||||
CHECK("globalObject() is object", Escargot::ValueRef::create(globalObject)->isObject());
|
||||
Escargot::ValueRef* jsmath
|
||||
= globalObject->get(es, Escargot::ValueRef::create(Escargot::StringRef::fromASCII("Math")));
|
||||
Escargot::ValueRef* jspi
|
||||
= jsmath->toObject(es)->get(es, Escargot::ValueRef::create(Escargot::StringRef::fromASCII("PI")));
|
||||
printf("Math.PI = %f\n", jspi->toNumber(es));
|
||||
|
||||
// custom function & NativeDataAccessorProperty & virtal-id test & ExposableObject test
|
||||
{
|
||||
Escargot::FunctionObjectRef::NativeFunctionInfo info(Escargot::AtomicStringRef::create(ctx, "Custom"), [](Escargot::ExecutionStateRef* state, Escargot::ValueRef* thisValue, size_t argc, Escargot::ValueRef** argv, bool isNewExpression) -> Escargot::ValueRef* {
|
||||
puts("custom function called");
|
||||
return Escargot::ValueRef::createUndefined();
|
||||
}, 0, [](Escargot::ExecutionStateRef* state, size_t argc, Escargot::ValueRef** argv) -> Escargot::ObjectRef* {
|
||||
puts("custom ctor called");
|
||||
return Escargot::ObjectRef::create(state);
|
||||
});
|
||||
Escargot::FunctionObjectRef* fn = Escargot::FunctionObjectRef::create(es, info);
|
||||
|
||||
Escargot::ObjectRef::NativeDataAccessorPropertyData* nativeData = new Escargot::ObjectRef::NativeDataAccessorPropertyData(true, true, true, [](Escargot::ExecutionStateRef* state, Escargot::ObjectRef* self, Escargot::ObjectRef::NativeDataAccessorPropertyData* data) -> Escargot::ValueRef* {
|
||||
puts("native getter called");
|
||||
return Escargot::ValueRef::create(120);
|
||||
}, [](Escargot::ExecutionStateRef* state, Escargot::ObjectRef* self, Escargot::ObjectRef::NativeDataAccessorPropertyData* data, Escargot::ValueRef* setterInputData) -> bool {
|
||||
puts("native Setter called");
|
||||
return true;
|
||||
});
|
||||
fn->defineNativeDataAccessorProperty(es, Escargot::ValueRef::create(Escargot::StringRef::fromASCII("native")), nativeData);
|
||||
|
||||
globalObject->set(es, Escargot::ValueRef::create(Escargot::StringRef::fromASCII("Custom")), Escargot::ValueRef::create(fn));
|
||||
|
||||
ctx->setVirtualIdentifierCallback([](Escargot::ExecutionStateRef* state, Escargot::ValueRef* name) -> Escargot::ValueRef* {
|
||||
if (name->toString(state)->equals(Escargot::StringRef::fromASCII("virtualid"))) {
|
||||
return Escargot::ValueRef::create(32);
|
||||
}
|
||||
return Escargot::ValueRef::createEmpty();
|
||||
});
|
||||
|
||||
ctx->setVirtualIdentifierInGlobalCallback([](Escargot::ExecutionStateRef* state, Escargot::ValueRef* name) -> Escargot::ValueRef* {
|
||||
if (name->toString(state)->equals(Escargot::StringRef::fromASCII("virtualidglobal"))) {
|
||||
return Escargot::ValueRef::create(64);
|
||||
}
|
||||
return Escargot::ValueRef::createEmpty();
|
||||
});
|
||||
|
||||
Escargot::ObjectRef* exp = Escargot::ObjectRef::createExposableObject(es, [](Escargot::ExecutionStateRef* state, Escargot::ObjectRef* self, Escargot::ValueRef* propertyName) -> Escargot::ValueRef* {
|
||||
if (propertyName->toString(state)->equals(Escargot::StringRef::fromASCII("virtualid"))) {
|
||||
return Escargot::ValueRef::create(Escargot::StringRef::fromASCII("virtualidvalue"));
|
||||
}
|
||||
return Escargot::ValueRef::createEmpty();
|
||||
}, [](Escargot::ExecutionStateRef* state, Escargot::ObjectRef* self, Escargot::ValueRef* propertyName, Escargot::ValueRef* value) {
|
||||
|
||||
}, [](Escargot::ExecutionStateRef* state, Escargot::ObjectRef* self) -> Escargot::ValueVectorRef* {
|
||||
return Escargot::ValueVectorRef::create(0);
|
||||
}, true, true, true);
|
||||
|
||||
globalObject->set(es, Escargot::ValueRef::create(Escargot::StringRef::fromASCII("exposableObject")), Escargot::ValueRef::create(exp));
|
||||
|
||||
const char* script = "print(virtualid); print(this.virtualidglobal); this.Custom.native = this.Custom.native; this.Custom(); new Custom(); print(exposableObject.virtualid);";
|
||||
const char* filename = "FileName.js";
|
||||
printf("evaluateScript %s\n", script);
|
||||
|
||||
Escargot::ScriptRef* scriptRef = ctx->scriptParser()->parse(Escargot::StringRef::fromASCII(script, strlen(script)), Escargot::StringRef::fromASCII(filename, strlen(filename))).m_script;
|
||||
Escargot::SandBoxRef* sb = Escargot::SandBoxRef::create(ctx);
|
||||
auto sandBoxResult = sb->run([&](Escargot::ExecutionStateRef* state) -> Escargot::ValueRef* {
|
||||
return scriptRef->execute(state);
|
||||
});
|
||||
|
||||
Escargot::ValueRef* evalResult = sandBoxResult.result;
|
||||
sb->destroy();
|
||||
}
|
||||
|
||||
es->destroy();
|
||||
ctx->destroy();
|
||||
vm->destroy();
|
||||
|
||||
Escargot::Globals::finalize();
|
||||
|
||||
printf("testapi ended\n");
|
||||
|
||||
return 0;
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
TEST(EvalScript, Run) {
|
||||
auto s = evalScript(g_context.get(), StringRef::createFromASCII("1 + 1"), StringRef::createFromASCII("test.js"), false);
|
||||
EXPECT_EQ(s, "2");
|
||||
}
|
||||
|
||||
TEST(EvalScript, Run2) {
|
||||
auto s = evalScript(g_context.get(), StringRef::createFromASCII("'1' - 1"), StringRef::createFromASCII("test.js"), false);
|
||||
EXPECT_EQ(s, "0");
|
||||
}
|
||||
|
||||
TEST(EvalScript, ParseError) {
|
||||
auto s = evalScript(g_context.get(), StringRef::createFromASCII("."), StringRef::createFromASCII("test.js"), false);
|
||||
EXPECT_TRUE(s.find("SyntaxError") != std::string::npos);
|
||||
}
|
||||
|
||||
TEST(EvalScript, RuntimeError) {
|
||||
auto s = evalScript(g_context.get(), StringRef::createFromASCII("throw 1"), StringRef::createFromASCII("test.js"), false);
|
||||
EXPECT_TRUE(s.find("Uncaught 1") == 0);
|
||||
}
|
||||
|
||||
TEST(ObjectTemplate, Basic1) {
|
||||
ObjectTemplateRef* tpl = ObjectTemplateRef::create();
|
||||
tpl->set(StringRef::createFromASCII("asdf"), StringRef::createFromASCII("asdfData"), false, false, false);
|
||||
|
||||
ObjectTemplateRef* another = ObjectTemplateRef::create();
|
||||
tpl->set(StringRef::createFromASCII("another"), another, false, false, false);
|
||||
|
||||
|
||||
ObjectRef* obj = tpl->instantiate(g_context.get());
|
||||
|
||||
Evaluator::execute(g_context.get(), [](ExecutionStateRef* state, ObjectRef* obj) -> ValueRef* {
|
||||
auto desc = obj->getOwnPropertyDescriptor(state, StringRef::createFromASCII("asdf"));
|
||||
auto value = desc->asObject()->get(state, StringRef::createFromASCII("value"));
|
||||
EXPECT_TRUE(value->asString()->equalsWithASCIIString("asdfData", 8));
|
||||
|
||||
value = desc->asObject()->get(state, StringRef::createFromASCII("writable"));
|
||||
EXPECT_TRUE(value->isFalse());
|
||||
|
||||
value = desc->asObject()->get(state, StringRef::createFromASCII("enumerable"));
|
||||
EXPECT_TRUE(value->isFalse());
|
||||
|
||||
value = desc->asObject()->get(state, StringRef::createFromASCII("configurable"));
|
||||
EXPECT_TRUE(value->isFalse());
|
||||
|
||||
desc = obj->getOwnPropertyDescriptor(state, StringRef::createFromASCII("another"));
|
||||
EXPECT_TRUE(desc->asObject()->get(state, StringRef::createFromASCII("value"))->isObject());
|
||||
|
||||
value = desc->asObject()->get(state, StringRef::createFromASCII("writable"));
|
||||
EXPECT_TRUE(value->isFalse());
|
||||
|
||||
value = desc->asObject()->get(state, StringRef::createFromASCII("enumerable"));
|
||||
EXPECT_TRUE(value->isFalse());
|
||||
|
||||
value = desc->asObject()->get(state, StringRef::createFromASCII("configurable"));
|
||||
EXPECT_TRUE(value->isFalse());
|
||||
|
||||
return ValueRef::createUndefined();
|
||||
}, obj);
|
||||
}
|
||||
|
||||
TEST(ObjectTemplate, Basic2) {
|
||||
ObjectTemplateRef* tpl = ObjectTemplateRef::create();
|
||||
|
||||
auto getter = FunctionTemplateRef::create(AtomicStringRef::emptyAtomicString(), 1, true, true, [](ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall) -> ValueRef* {
|
||||
return ValueRef::create(12);
|
||||
}, nullptr);
|
||||
|
||||
tpl->setAccessorProperty(StringRef::createFromASCII("asdf"), getter, nullptr, false, true);
|
||||
|
||||
auto getter2 = FunctionTemplateRef::create(AtomicStringRef::emptyAtomicString(), 1, true, true, [](ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall) -> ValueRef* {
|
||||
return (ValueRef*)thisValue->asObject()->extraData();
|
||||
}, nullptr);
|
||||
auto setter = FunctionTemplateRef::create(AtomicStringRef::emptyAtomicString(), 1, true, true, [](ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall) -> ValueRef* {
|
||||
thisValue->asObject()->setExtraData(argv[0]);
|
||||
return ValueRef::createUndefined();
|
||||
}, nullptr);
|
||||
tpl->setAccessorProperty(StringRef::createFromASCII("asdf2"), getter2, setter, false, true);
|
||||
|
||||
ObjectRef* obj = tpl->instantiate(g_context.get());
|
||||
|
||||
Evaluator::execute(g_context.get(), [](ExecutionStateRef* state, ObjectRef* obj) -> ValueRef* {
|
||||
auto desc = obj->getOwnPropertyDescriptor(state, StringRef::createFromASCII("asdf"));
|
||||
|
||||
auto value = desc->asObject()->get(state, StringRef::createFromASCII("enumerable"));
|
||||
EXPECT_TRUE(value->isFalse());
|
||||
|
||||
value = desc->asObject()->get(state, StringRef::createFromASCII("configurable"));
|
||||
EXPECT_TRUE(value->isTrue());
|
||||
|
||||
value = desc->asObject()->get(state, StringRef::createFromASCII("get"));
|
||||
EXPECT_TRUE(value->isFunctionObject());
|
||||
|
||||
value = desc->asObject()->get(state, StringRef::createFromASCII("set"));
|
||||
EXPECT_TRUE(value->isUndefined());
|
||||
|
||||
EXPECT_TRUE(obj->get(state, StringRef::createFromASCII("asdf"))->equalsTo(state, ValueRef::create(12)));
|
||||
|
||||
obj->set(state, StringRef::createFromASCII("asdf2"), StringRef::createFromASCII("test"));
|
||||
EXPECT_TRUE(obj->get(state, StringRef::createFromASCII("asdf2"))->equalsTo(state, StringRef::createFromASCII("test")));
|
||||
|
||||
return ValueRef::createUndefined();
|
||||
}, obj);
|
||||
}
|
||||
|
||||
TEST(ObjectTemplate, Basic3) {
|
||||
ObjectTemplateRef* tpl = ObjectTemplateRef::create();
|
||||
|
||||
class TestNativeDataAccessorPropertyData : public ObjectRef::NativeDataAccessorPropertyData {
|
||||
public:
|
||||
TestNativeDataAccessorPropertyData()
|
||||
: ObjectRef::NativeDataAccessorPropertyData(false, false, false, nullptr, nullptr)
|
||||
{
|
||||
number = 10;
|
||||
}
|
||||
double number;
|
||||
};
|
||||
|
||||
TestNativeDataAccessorPropertyData* data = new TestNativeDataAccessorPropertyData();
|
||||
data->m_isConfigurable = false;
|
||||
data->m_isEnumerable = false;
|
||||
data->m_isWritable = true;
|
||||
data->m_getter = [](ExecutionStateRef* state, ObjectRef* self, ObjectRef::NativeDataAccessorPropertyData* data) -> ValueRef* {
|
||||
return ValueRef::create(((TestNativeDataAccessorPropertyData*)data)->number);
|
||||
};
|
||||
|
||||
data->m_setter = [](ExecutionStateRef* state, ObjectRef* self, ObjectRef::NativeDataAccessorPropertyData* data, ValueRef* setterInputData) -> bool {
|
||||
((TestNativeDataAccessorPropertyData*)data)->number = setterInputData->toNumber(state);
|
||||
return true;
|
||||
};
|
||||
|
||||
tpl->setNativeDataAccessorProperty(StringRef::createFromASCII("asdf"), data);
|
||||
|
||||
ObjectRef* obj = tpl->instantiate(g_context.get());
|
||||
|
||||
Evaluator::execute(g_context.get(), [](ExecutionStateRef* state, ObjectRef* obj) -> ValueRef* {
|
||||
auto desc = obj->getOwnPropertyDescriptor(state, StringRef::createFromASCII("asdf"));
|
||||
|
||||
auto value = desc->asObject()->get(state, StringRef::createFromASCII("enumerable"));
|
||||
EXPECT_TRUE(value->isFalse());
|
||||
|
||||
value = desc->asObject()->get(state, StringRef::createFromASCII("configurable"));
|
||||
EXPECT_TRUE(value->isFalse());
|
||||
|
||||
value = desc->asObject()->get(state, StringRef::createFromASCII("writable"));
|
||||
EXPECT_TRUE(value->isTrue());
|
||||
|
||||
obj->set(state, StringRef::createFromASCII("asdf"), ValueRef::create(20));
|
||||
|
||||
EXPECT_TRUE(obj->get(state, StringRef::createFromASCII("asdf"))->equalsTo(state, ValueRef::create(20)));
|
||||
|
||||
return ValueRef::createUndefined();
|
||||
}, obj);
|
||||
}
|
||||
|
||||
TEST(FunctionTemplate, Basic1) {
|
||||
auto ft = FunctionTemplateRef::create(AtomicStringRef::create(g_context.get(), "asdf"), 2, true, true, [](ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall) -> ValueRef* {
|
||||
EXPECT_TRUE(argc == 1);
|
||||
return argv[0];
|
||||
}, nullptr);
|
||||
|
||||
FunctionObjectRef* fn = ft->instantiate(g_context.get())->asFunctionObject();
|
||||
|
||||
// same instance on same context
|
||||
EXPECT_EQ(fn, ft->instantiate(g_context.get())->asFunctionObject());
|
||||
|
||||
Evaluator::execute(g_context.get(), [](ExecutionStateRef* state, FunctionObjectRef* fn) -> ValueRef* {
|
||||
ValueRef* arr[1] = { ValueRef::create(123) };
|
||||
EXPECT_TRUE(fn->call(state, ValueRef::createUndefined(), 1, arr)->equalsTo(state, ValueRef::create(123)));
|
||||
return ValueRef::createUndefined();
|
||||
}, fn);
|
||||
}
|
||||
|
||||
TEST(FunctionTemplate, Basic2) {
|
||||
auto ft = FunctionTemplateRef::create(AtomicStringRef::create(g_context.get(), "parent"), 0,
|
||||
true, true, [](ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall) -> ValueRef* {
|
||||
return ValueRef::createUndefined();
|
||||
}, nullptr);
|
||||
ft->prototypeTemplate()->set(StringRef::createFromASCII("asdf1"), ValueRef::create(1), true, true, true);
|
||||
|
||||
auto ftchildobj = ObjectTemplateRef::create();
|
||||
ftchildobj->set(StringRef::createFromASCII("asdf"), ValueRef::create(0), true, true, true);
|
||||
auto ftchild = FunctionTemplateRef::create(AtomicStringRef::create(g_context.get(), "asdf"), 2,
|
||||
true, true, [](ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall) -> ValueRef* {
|
||||
return ValueRef::create(123);
|
||||
}, ftchildobj);
|
||||
|
||||
ftchild->prototypeTemplate()->set(StringRef::createFromASCII("asdf2"), ValueRef::create(2), true, true, true);
|
||||
ftchild->inherit(ft);
|
||||
|
||||
Evaluator::execute(g_context.get(), [](ExecutionStateRef* state, FunctionTemplateRef* ftchild) -> ValueRef* {
|
||||
ObjectRef* ref = ftchild->instantiate(g_context.get())->construct(state, 0, 0);
|
||||
|
||||
EXPECT_TRUE(ref->get(state, StringRef::createFromASCII("asdf"))->equalsTo(state, ValueRef::create(0)));
|
||||
EXPECT_TRUE(ref->hasOwnProperty(state, StringRef::createFromASCII("asdf")));
|
||||
EXPECT_TRUE(ref->get(state, StringRef::createFromASCII("asdf1"))->equalsTo(state, ValueRef::create(1)));
|
||||
EXPECT_TRUE(ref->get(state, StringRef::createFromASCII("asdf2"))->equalsTo(state, ValueRef::create(2)));
|
||||
|
||||
return ValueRef::createUndefined();
|
||||
}, ftchild);
|
||||
}
|
||||
|
||||
|
|
|
|||
1
third_party/googletest
vendored
Submodule
1
third_party/googletest
vendored
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 2fe3bd994b3189899d93f1d5a881e725e046fdc2
|
||||
|
|
@ -507,6 +507,16 @@ def run_intl(engine, arch):
|
|||
if fails > 0:
|
||||
raise Exception('Intl tests failed')
|
||||
|
||||
@runner('cctest', default=False)
|
||||
def run_cctest(engine, arch):
|
||||
if engine is "escargot":
|
||||
engine = "cctest"
|
||||
proc = Popen([engine], stdout=PIPE)
|
||||
stdout, _ = proc.communicate()
|
||||
print(stdout)
|
||||
if ' PASSED ' not in stdout:
|
||||
raise Exception('Not all tests succeeded')
|
||||
|
||||
@runner('debugger-server-source', default=True)
|
||||
def run_escargot_debugger(engine, arch):
|
||||
ESCARGOT_DEBUGGER_TEST_DIR = join(PROJECT_SOURCE_DIR, 'tools', 'debugger', 'tests')
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue