现在的webkit分为4个Floder:Javascript Folder,Tools,WebCore Folder,Webkit2 Folder,WebkitCom Folder,这5个目录的编译顺序基本是:Javascript Folder-》WebCore Folder-》WebkitCom Folder-》Webkit2 Folder-》Tools(有些子工程的顺序并不是这样的),下面就以这几个目录为单位来说明webkit的编译规则。
1. Javascript Folder
这个目录下有5个工程:JavaScriptCore, JavaScriptGenerated, jsc, testapi, WTF。编译顺序是:JavaScriptGenerated-》WTF-》 JavaScriptCore-》testapi-》jsc。testapi和jsc其实是在比较后面编译的,应为它们依赖于Tools下的FindSafari工程。
首先看JavaScriptGenerated的工程属性,可以看到这个工程是用nmake编译的,如下图:
JavaScriptGenerated的工程文件在D:/tools/cygwin/home/xufan/WebKit/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCoreGeneratedCommon.vsprops,内容如下:
<?xml version="1.0" encoding="Windows-1252"?> <VisualStudioPropertySheet ProjectType="Visual C++" Version="8.00" Name="JavaScriptCoreGeneratedCommon" OutputDirectory="$(WebKitOutputDir)/lib" IntermediateDirectory="$(WebKitOutputDir)/obj/$(ProjectName)/$(ConfigurationName)" > <Tool Name="VCNMakeTool" BuildCommandLine="%SystemDrive%/cygwin/bin/which.exe bash if errorlevel 1 set PATH=%SystemDrive%/cygwin/bin;%PATH% cmd /c nmake /nologo -f JavaScriptCoreGenerated.make" ReBuildCommandLine="%SystemDrive%/cygwin/bin/which.exe bash if errorlevel 1 set PATH=%SystemDrive%/cygwin/bin;%PATH% cmd /c nmake /nologo -f JavaScriptCoreGenerated.make clean nmake -f JavaScriptCoreGenerated.make" CleanCommandLine="%SystemDrive%/cygwin/bin/which.exe bash if errorlevel 1 set PATH=%SystemDrive%/cygwin/bin;%PATH% cmd /c nmake /nologo -f JavaScriptCoreGenerated.make clean" /> </VisualStudioPropertySheet>
其中编译这个工程使用的命令行(BuildCommandLine)为:
%SystemDrive%/cygwin/bin/which.exe bash
if errorlevel 1 set PATH=%SystemDrive%/cygwin/bin;%PATH%
cmd /c
nmake /nologo -f JavaScriptCoreGenerated.make
从上面可以看到,首先执行which命令在环境变量PATH里面查找bash的位置,如果查找不成功,则将cygwin下面的bin目录加到环境变量里面PATH。接着调用cmd /c, 表示当执行完相应的命令(在命令提示符中)后自动退出命令提示符,这个接的命令为空,应该只是用于刷新命令行环境。最后用nmake去编译JavaScriptCoreGenerated.make这个文件了,JavaScriptCoreGenerated.make文件内容如下:
all: touch "$(WEBKITOUTPUTDIR)/buildfailed" bash build-generated-files.sh "$(WEBKITOUTPUTDIR)" "$(WEBKITLIBRARIESDIR)" -bash -c "python react-to-vsprops-changes.py" -mkdir 2>NUL "$(WEBKITOUTPUTDIR)/include/JavaScriptCore" xcopy /y /d "../../API/APICast.h" "$(WEBKITOUTPUTDIR)/include/JavaScriptCore" xcopy /y /d "../../API/JavaScript.h" "$(WEBKITOUTPUTDIR)/include/JavaScriptCore" xcopy /y /d "../../API/JSBase.h" "$(WEBKITOUTPUTDIR)/include/JavaScriptCore" xcopy /y /d "../../API/JSContextRef.h" "$(WEBKITOUTPUTDIR)/include/JavaScriptCore" xcopy /y /d "../../API/JSContextRefPrivate.h" "$(WEBKITOUTPUTDIR)/include/JavaScriptCore" xcopy /y /d "../../API/JSObjectRef.h" "$(WEBKITOUTPUTDIR)/include/JavaScriptCore" xcopy /y /d "../../API/JSObjectRefPrivate.h" "$(WEBKITOUTPUTDIR)/include/JavaScriptCore" xcopy /y /d "../../API/JSStringRef.h" "$(WEBKITOUTPUTDIR)/include/JavaScriptCore" xcopy /y /d "../../API/JSStringRefCF.h" "$(WEBKITOUTPUTDIR)/include/JavaScriptCore" xcopy /y /d "../../API/JSStringRefBSTR.h" "$(WEBKITOUTPUTDIR)/include/JavaScriptCore" xcopy /y /d "../../API/JSValueRef.h" "$(WEBKITOUTPUTDIR)/include/JavaScriptCore" xcopy /y /d "../../API/JavaScriptCore.h" "$(WEBKITOUTPUTDIR)/include/JavaScriptCore" xcopy /y /d "../../API/JSRetainPtr.h" "$(WEBKITOUTPUTDIR)/include/JavaScriptCore" xcopy /y /d "../../API/JSWeakObjectMapRefInternal.h" "$(WEBKITOUTPUTDIR)/include/JavaScriptCore" xcopy /y /d "../../API/JSWeakObjectMapRefPrivate.h" "$(WEBKITOUTPUTDIR)/include/JavaScriptCore" xcopy /y /d "../../API/JSRetainPtr.h" "$(WEBKITOUTPUTDIR)/include/JavaScriptCore" xcopy /y /d "../../API/OpaqueJSString.h" "$(WEBKITOUTPUTDIR)/include/JavaScriptCore" xcopy /y /d "../../API/WebKitAvailability.h" "$(WEBKITOUTPUTDIR)/include/JavaScriptCore" -mkdir 2>NUL "$(WEBKITOUTPUTDIR)/include/private/JavaScriptCore" xcopy /y /d "../../wtf/*.h" "$(WEBKITOUTPUTDIR)/include/private/JavaScriptCore" xcopy /y /d "../../wtf/text/*.h" "$(WEBKITOUTPUTDIR)/include/private/JavaScriptCore" xcopy /y /d "../../wtf/unicode/*.h" "$(WEBKITOUTPUTDIR)/include/private/JavaScriptCore" xcopy /y /d "../../wtf/unicode/icu/*.h" "$(WEBKITOUTPUTDIR)/include/private/JavaScriptCore" xcopy /y /d "../../parser/*.h" "$(WEBKITOUTPUTDIR)/include/private/JavaScriptCore" xcopy /y /d "../../runtime/*.h" "$(WEBKITOUTPUTDIR)/include/private/JavaScriptCore" xcopy /y /d "../../bytecode/*.h" "$(WEBKITOUTPUTDIR)/include/private/JavaScriptCore" xcopy /y /d "../../interpreter/*.h" "$(WEBKITOUTPUTDIR)/include/private/JavaScriptCore" xcopy /y /d "../../assembler/*.h" "$(WEBKITOUTPUTDIR)/include/private/JavaScriptCore" xcopy /y /d "../../jit/*.h" "$(WEBKITOUTPUTDIR)/include/private/JavaScriptCore" xcopy /y /d "../../debugger/*.h" "$(WEBKITOUTPUTDIR)/include/private/JavaScriptCore" xcopy /y /d "../../profiler/*.h" "$(WEBKITOUTPUTDIR)/include/private/JavaScriptCore" xcopy /y /d "../../create_hash_table" "$(WEBKITOUTPUTDIR)/include/private/JavaScriptCore" xcopy /y /d "../../pcre/pcre.h" "$(WEBKITOUTPUTDIR)/include/private/JavaScriptCore" xcopy /y /d "../../wtf/text/AtomicString.cpp" "$(WEBKITOUTPUTDIR)/include/private/JavaScriptCore" xcopy /y /d "../../wtf/text/StringBuilder.cpp" "$(WEBKITOUTPUTDIR)/include/private/JavaScriptCore" xcopy /y /d "../../wtf/text/StringImpl.cpp" "$(WEBKITOUTPUTDIR)/include/private/JavaScriptCore" xcopy /y /d "../../wtf/text/WTFString.cpp" "$(WEBKITOUTPUTDIR)/include/private/JavaScriptCore" -mkdir 2>NUL "$(WEBKITOUTPUTDIR)/bin/JavaScriptCore.resources" xcopy /y /d "../JavaScriptCore.resources/*" "$(WEBKITOUTPUTDIR)/bin/JavaScriptCore.resources" -del "$(WEBKITOUTPUTDIR)/include/private/JavaScriptCore/stdbool.h" "$(WEBKITOUTPUTDIR)/include/private/JavaScriptCore/stdint.h" -del "$(WEBKITOUTPUTDIR)/buildfailed" clean: -del "$(WEBKITOUTPUTDIR)/buildfailed" -del /s /q "$(WEBKITOUTPUTDIR)/include/JavaScriptCore/JavaScriptCore" -del /s /q "$(WEBKITOUTPUTDIR)/obj/JavaScriptCore/DerivedSources" -del /s /q "$(WEBKITOUTPUTDIR)/include/private/JavaScriptCore" -del /s /q "$(WEBKITOUTPUTDIR)/bin/JavaScriptCore.resources"
all规则首先touch $(WEBKITOUTPUTDIR)/buildfailed这个文件,用来更新这个文件的存取时间和更改时间,因为后面有makefile规则依赖于这个文件,这个文件的作用是用来保存编译失败的信息,如果webkit编译失败,可以到这个文件里面看失败的信息。
接着调用build-generated-files.sh这个perl脚本(D:/tools/cygwin/home/xufan/WebKit/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/build-generated-files.sh),脚本内容如下:
#!/usr/bin/bash # Determine whether we have the versioned ICU 4.0 or the unversioned ICU 4.4 UNVERSIONED_ICU_LIB_PATH=$(cygpath -u "${WEBKITLIBRARIESDIR}/lib/libicuuc.lib") ICUVERSION_H_PATH=$(cygpath -u "${WEBKITOUTPUTDIR}/include/private/ICUVersion.h") if test /( ! -f "${ICUVERSION_H_PATH}" /) -o /( -f "${UNVERSIONED_ICU_LIB_PATH}" -a /( "${UNVERSIONED_ICU_LIB_PATH}" -nt "${ICUVERSION_H_PATH}" /) /) then mkdir -p "$(dirname "${ICUVERSION_H_PATH}")" test ! -f "${UNVERSIONED_ICU_LIB_PATH}" echo "#define U_DISABLE_RENAMING $?" > "${ICUVERSION_H_PATH}" fi NUMCPUS=`../../../Tools/Scripts/num-cpus` XSRCROOT="`pwd`/../.." XSRCROOT=`realpath "$XSRCROOT"` # Do a little dance to get the path into 8.3 form to make it safe for gnu make # http://bugzilla.opendarwin.org/show_bug.cgi?id=8173 XSRCROOT=`cygpath -m -s "$XSRCROOT"` XSRCROOT=`cygpath -u "$XSRCROOT"` export XSRCROOT export SOURCE_ROOT=$XSRCROOT XDSTROOT="$1" export XDSTROOT # Do a little dance to get the path into 8.3 form to make it safe for gnu make # http://bugzilla.opendarwin.org/show_bug.cgi?id=8173 XDSTROOT=`cygpath -m -s "$XDSTROOT"` XDSTROOT=`cygpath -u "$XDSTROOT"` export XDSTROOT SDKROOT="$2" export SDKROOT # Do a little dance to get the path into 8.3 form to make it safe for gnu make # http://bugzilla.opendarwin.org/show_bug.cgi?id=8173 SDKROOT=`cygpath -m -s "$SDKROOT"` SDKROOT=`cygpath -u "$SDKROOT"` export SDKROOT export BUILT_PRODUCTS_DIR="$XDSTROOT/obj/JavaScriptCore" mkdir -p "${BUILT_PRODUCTS_DIR}/DerivedSources/docs" cd "${BUILT_PRODUCTS_DIR}/DerivedSources" export JavaScriptCore="${XSRCROOT}" export DFTABLES_EXTENSION=".exe" make -f "$JavaScriptCore/DerivedSources.make" -j ${NUMCPUS} || exit 1
这个脚本里面主要是设置一些跟各种目录相关的环境变量,里面用cygpath命令将路径在windows形式(c:/windows)和cygwin形式(/cygdrive/c/windows)作了转换。里面用了num-cpus脚本去获取cpu的个数。最后调用make工具编译$JavaScriptCore/DerivedSources.make这个文件(D:/tools/cygwin/home/xufan/WebKit/JavaScriptCore/DerivedSources.make):
VPATH = / $(JavaScriptCore) / $(JavaScriptCore)/parser / $(JavaScriptCore)/pcre / $(JavaScriptCore)/docs / $(JavaScriptCore)/runtime / $(JavaScriptCore)/interpreter / $(JavaScriptCore)/jit / # .PHONY : all all : / ArrayPrototype.lut.h / chartables.c / DatePrototype.lut.h / JSONObject.lut.h / Lexer.lut.h / MathObject.lut.h / NumberConstructor.lut.h / RegExpConstructor.lut.h / RegExpObject.lut.h / StringPrototype.lut.h / docs/bytecode.html / RegExpJitTables.h / JavaScriptCore.JSVALUE32.exp / JavaScriptCore.JSVALUE32_64.exp / JavaScriptCore.JSVALUE64.exp / # # lookup tables for classes %.lut.h: create_hash_table %.cpp $^ -i > $@ Lexer.lut.h: create_hash_table Keywords.table $^ > $@ # character tables for PCRE chartables.c : dftables $^ $@ docs/bytecode.html: make-bytecode-docs.pl Interpreter.cpp perl $^ $@ #character tables for Yarr RegExpJitTables.h: create_regex_tables python $^ > $@ JavaScriptCore.JSVALUE32.exp: JavaScriptCore.exp JavaScriptCore.JSVALUE32only.exp cat $^ > $@ JavaScriptCore.JSVALUE32_64.exp: JavaScriptCore.exp JavaScriptCore.JSVALUE32_64only.exp cat $^ > $@ JavaScriptCore.JSVALUE64.exp: JavaScriptCore.exp JavaScriptCore.JSVALUE64only.exp cat $^ > $@
makefile中VPATH的作用有点像命令行中path这个环境变量,make默认只会在当前的目录中去找寻依赖文件和目标文件,如果定义了VPATH,那么make会在当当前目录找不到的情况下,到VPATH定义的目录中去找。
从上面的makefile可以看出,all规则依赖于ArrayPrototype.lut.h,chartables.c,JavaScriptCore.JSVALUE32.exp等文件,*.lut.h等文件的编译规则是:
%.lut.h: create_hash_table %.cpp
$^ -i > $@
对于ArrayPrototype.lut.h这个文件,这条编译规则展开后就是:
ArrayPrototype.lut.h: create_hash_table ArrayPrototype.cpp
create_hash_table ArrayPrototype.cpp –i > ArrayPrototype.lut.h
也就是说ArrayPrototype.lut.h依赖于create_hash_table和ArrayPrototype.cpp,编译所采取的action为create_hash_table ArrayPrototype.cpp –i > ArrayPrototype.lut.h,即调用create_hash_table ArrayPrototype.cpp –i来生成ArrayPrototype.lut.h这个文件。
create_hash_table的路径是D:/tools/cygwin/home/xufan/WebKit/JavaScriptCore/create_hash_table,是一个perl脚本,它的作用是分析一个cpp文件的注释中含有@begin @end的部分,然后生成一个用于向js引擎注入对象所有要求的结构体形式的头文件,例如将D:/tools/cygwin/home/xufan/WebKit/JavaScriptCore/runtime/ArrayPrototype.cpp的内容如下(特别注意90到112行):
/* * Copyright (C) 1999-2000 Harri Porten ([email protected]) * Copyright (C) 2003, 2007, 2008, 2009 Apple Inc. All rights reserved. * Copyright (C) 2003 Peter Kelly ([email protected]) * Copyright (C) 2006 Alexey Proskuryakov ([email protected]) * * 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 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 "config.h" #include "ArrayPrototype.h" #include "CachedCall.h" #include "CodeBlock.h" #include "Interpreter.h" #include "JIT.h" #include "JSStringBuilder.h" #include "Lookup.h" #include "ObjectPrototype.h" #include "Operations.h" #include #include #include namespace JSC { ASSERT_CLASS_FITS_IN_CELL(ArrayPrototype); static EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState*); static EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState*); static EncodedJSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState*); static EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState*); static EncodedJSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState*); static EncodedJSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState*); static EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState*); static EncodedJSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState*); static EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState*); static EncodedJSValue JSC_HOST_CALL arrayProtoFuncSort(ExecState*); static EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState*); static EncodedJSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState*); static EncodedJSValue JSC_HOST_CALL arrayProtoFuncEvery(ExecState*); static EncodedJSValue JSC_HOST_CALL arrayProtoFuncForEach(ExecState*); static EncodedJSValue JSC_HOST_CALL arrayProtoFuncSome(ExecState*); static EncodedJSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState*); static EncodedJSValue JSC_HOST_CALL arrayProtoFuncFilter(ExecState*); static EncodedJSValue JSC_HOST_CALL arrayProtoFuncMap(ExecState*); static EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduce(ExecState*); static EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduceRight(ExecState*); static EncodedJSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState*); } #include "ArrayPrototype.lut.h" namespace JSC { static inline bool isNumericCompareFunction(ExecState* exec, CallType callType, const CallData& callData) { if (callType != CallTypeJS) return false; FunctionExecutable* executable = callData.js.functionExecutable; JSObject* error = executable->compileForCall(exec, callData.js.scopeChain); if (error) return false; return executable->generatedBytecodeForCall().isNumericCompareFunction(); } // ------------------------------ ArrayPrototype ---------------------------- const ClassInfo ArrayPrototype::info = {"Array", &JSArray::info, 0, ExecState::arrayTable}; /* Source for ArrayPrototype.lut.h @begin arrayTable 16 toString arrayProtoFuncToString DontEnum|Function 0 toLocaleString arrayProtoFuncToLocaleString DontEnum|Function 0 concat arrayProtoFuncConcat DontEnum|Function 1 join arrayProtoFuncJoin DontEnum|Function 1 pop arrayProtoFuncPop DontEnum|Function 0 push arrayProtoFuncPush DontEnum|Function 1 reverse arrayProtoFuncReverse DontEnum|Function 0 shift arrayProtoFuncShift DontEnum|Function 0 slice arrayProtoFuncSlice DontEnum|Function 2 sort arrayProtoFuncSort DontEnum|Function 1 splice arrayProtoFuncSplice DontEnum|Function 2 unshift arrayProtoFuncUnShift DontEnum|Function 1 every arrayProtoFuncEvery DontEnum|Function 1 forEach arrayProtoFuncForEach DontEnum|Function 1 some arrayProtoFuncSome DontEnum|Function 1 indexOf arrayProtoFuncIndexOf DontEnum|Function 1 lastIndexOf arrayProtoFuncLastIndexOf DontEnum|Function 1 filter arrayProtoFuncFilter DontEnum|Function 1 reduce arrayProtoFuncReduce DontEnum|Function 1 reduceRight arrayProtoFuncReduceRight DontEnum|Function 1 map arrayProtoFuncMap DontEnum|Function 1 @end */ // ECMA 15.4.4 ArrayPrototype::ArrayPrototype(JSGlobalObject* globalObject, NonNullPassRefPtr structure) : JSArray(structure) { putAnonymousValue(0, globalObject); } bool ArrayPrototype::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) { return getStaticFunctionSlot(exec, ExecState::arrayTable(exec), this, propertyName, slot); } bool ArrayPrototype::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) { return getStaticFunctionDescriptor(exec, ExecState::arrayTable(exec), this, propertyName, descriptor); } // ------------------------------ Array Functions ---------------------------- // Helper function static JSValue getProperty(ExecState* exec, JSObject* obj, unsigned index) { PropertySlot slot(obj); if (!obj->getPropertySlot(exec, index, slot)) return JSValue(); return slot.getValue(exec, index); } static void putProperty(ExecState* exec, JSObject* obj, const Identifier& propertyName, JSValue value) { PutPropertySlot slot; obj->put(exec, propertyName, value, slot); } static unsigned argumentClampedIndexFromStartOrEnd(ExecState* exec, int argument, unsigned length, unsigned undefinedValue = 0) { JSValue value = exec->argument(argument); if (value.isUndefined()) return undefinedValue; double indexDouble = value.toInteger(exec); if (indexDouble < 0) { indexDouble += length; return indexDouble < 0 ? 0 : static_cast(indexDouble); } return indexDouble > length ? length : static_cast(indexDouble); } EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec) { JSValue thisValue = exec->hostThisValue(); bool isRealArray = isJSArray(&exec->globalData(), thisValue); if (!isRealArray && !thisValue.inherits(&JSArray::info)) return throwVMTypeError(exec); JSArray* thisObj = asArray(thisValue); HashSet& arrayVisitedElements = exec->globalData().arrayVisitedElements; if (arrayVisitedElements.size() >= MaxSmallThreadReentryDepth) { if (arrayVisitedElements.size() >= exec->globalData().maxReentryDepth) return throwVMError(exec, createStackOverflowError(exec)); } bool alreadyVisited = !arrayVisitedElements.add(thisObj).second; if (alreadyVisited) return JSValue::encode(jsEmptyString(exec)); // return an empty string, avoiding infinite recursion. unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); unsigned totalSize = length ? length - 1 : 0; #if OS(SYMBIAN) // Symbian has very limited stack size available. // This function could be called recursively and allocating 1K on stack here cause // stack overflow on Symbian devices. Vector > strBuffer(length); #else Vector, 256> strBuffer(length); #endif for (unsigned k = 0; k < length; k++) { JSValue element; if (isRealArray && thisObj->canGetIndex(k)) element = thisObj->getIndex(k); else element = thisObj->get(exec, k); if (element.isUndefinedOrNull()) continue; UString str = element.toString(exec); strBuffer[k] = str.impl(); totalSize += str.length(); if (!strBuffer.data()) { throwOutOfMemoryError(exec); } if (exec->hadException()) break; } arrayVisitedElements.remove(thisObj); if (!totalSize) return JSValue::encode(jsEmptyString(exec)); Vector buffer; buffer.reserveCapacity(totalSize); if (!buffer.data()) return JSValue::encode(throwOutOfMemoryError(exec)); for (unsigned i = 0; i < length; i++) { if (i) buffer.append(','); if (RefPtr rep = strBuffer[i]) buffer.append(rep->characters(), rep->length()); } ASSERT(buffer.size() == totalSize); return JSValue::encode(jsString(exec, UString::adopt(buffer))); } EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState* exec) { JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&JSArray::info)) return throwVMTypeError(exec); JSObject* thisObj = asArray(thisValue); HashSet& arrayVisitedElements = exec->globalData().arrayVisitedElements; if (arrayVisitedElements.size() >= MaxSmallThreadReentryDepth) { if (arrayVisitedElements.size() >= exec->globalData().maxReentryDepth) return throwVMError(exec, createStackOverflowError(exec)); } bool alreadyVisited = !arrayVisitedElements.add(thisObj).second; if (alreadyVisited) return JSValue::encode(jsEmptyString(exec)); // return an empty string, avoding infinite recursion. JSStringBuilder strBuffer; unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); for (unsigned k = 0; k < length; k++) { if (k >= 1) strBuffer.append(','); JSValue element = thisObj->get(exec, k); if (!element.isUndefinedOrNull()) { JSObject* o = element.toObject(exec); JSValue conversionFunction = o->get(exec, exec->propertyNames().toLocaleString); UString str; CallData callData; CallType callType = getCallData(conversionFunction, callData); if (callType != CallTypeNone) str = call(exec, conversionFunction, callType, callData, element, exec->emptyList()).toString(exec); else str = element.toString(exec); strBuffer.append(str); } } arrayVisitedElements.remove(thisObj); return JSValue::encode(strBuffer.build(exec)); } EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState* exec) { JSObject* thisObj = exec->hostThisValue().toThisObject(exec); HashSet& arrayVisitedElements = exec->globalData().arrayVisitedElements; if (arrayVisitedElements.size() >= MaxSmallThreadReentryDepth) { if (arrayVisitedElements.size() >= exec->globalData().maxReentryDepth) return throwVMError(exec, createStackOverflowError(exec)); } bool alreadyVisited = !arrayVisitedElements.add(thisObj).second; if (alreadyVisited) return JSValue::encode(jsEmptyString(exec)); // return an empty string, avoding infinite recursion. JSStringBuilder strBuffer; UString separator; if (!exec->argument(0).isUndefined()) separator = exec->argument(0).toString(exec); unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); unsigned k = 0; if (isJSArray(&exec->globalData(), thisObj)) { JSArray* array = asArray(thisObj); if (length) { if (!array->canGetIndex(k)) goto skipFirstLoop; JSValue element = array->getIndex(k); if (!element.isUndefinedOrNull()) strBuffer.append(element.toString(exec)); k++; } if (separator.isNull()) { for (; k < length; k++) { if (!array->canGetIndex(k)) break; strBuffer.append(','); JSValue element = array->getIndex(k); if (!element.isUndefinedOrNull()) strBuffer.append(element.toString(exec)); } } else { for (; k < length; k++) { if (!array->canGetIndex(k)) break; strBuffer.append(separator); JSValue element = array->getIndex(k); if (!element.isUndefinedOrNull()) strBuffer.append(element.toString(exec)); } } } skipFirstLoop: for (; k < length; k++) { if (k >= 1) { if (separator.isNull()) strBuffer.append(','); else strBuffer.append(separator); } JSValue element = thisObj->get(exec, k); if (!element.isUndefinedOrNull()) strBuffer.append(element.toString(exec)); } arrayVisitedElements.remove(thisObj); return JSValue::encode(strBuffer.build(exec)); } EncodedJSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState* exec) { JSValue thisValue = exec->hostThisValue(); JSArray* arr = constructEmptyArray(exec); unsigned n = 0; JSValue curArg = thisValue.toThisObject(exec); size_t i = 0; size_t argCount = exec->argumentCount(); while (1) { if (curArg.inherits(&JSArray::info)) { unsigned length = curArg.get(exec, exec->propertyNames().length).toUInt32(exec); JSObject* curObject = curArg.toObject(exec); for (unsigned k = 0; k < length; ++k) { if (JSValue v = getProperty(exec, curObject, k)) arr->put(exec, n, v); n++; } } else { arr->put(exec, n, curArg); n++; } if (i == argCount) break; curArg = (exec->argument(i)); ++i; } arr->setLength(n); return JSValue::encode(arr); } EncodedJSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState* exec) { JSValue thisValue = exec->hostThisValue(); if (isJSArray(&exec->globalData(), thisValue)) return JSValue::encode(asArray(thisValue)->pop()); JSObject* thisObj = thisValue.toThisObject(exec); JSValue result; unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); if (length == 0) { putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length)); result = jsUndefined(); } else { result = thisObj->get(exec, length - 1); thisObj->deleteProperty(exec, length - 1); putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length - 1)); } return JSValue::encode(result); } EncodedJSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState* exec) { JSValue thisValue = exec->hostThisValue(); if (isJSArray(&exec->globalData(), thisValue) && exec->argumentCount() == 1) { JSArray* array = asArray(thisValue); array->push(exec, exec->argument(0)); return JSValue::encode(jsNumber(array->length())); } JSObject* thisObj = thisValue.toThisObject(exec); unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); for (unsigned n = 0; n < exec->argumentCount(); n++) thisObj->put(exec, length + n, exec->argument(n)); length += exec->argumentCount(); putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length)); return JSValue::encode(jsNumber(length)); } EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState* exec) { JSObject* thisObj = exec->hostThisValue().toThisObject(exec); unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); unsigned middle = length / 2; for (unsigned k = 0; k < middle; k++) { unsigned lk1 = length - k - 1; JSValue obj2 = getProperty(exec, thisObj, lk1); JSValue obj = getProperty(exec, thisObj, k); if (obj2) thisObj->put(exec, k, obj2); else thisObj->deleteProperty(exec, k); if (obj) thisObj->put(exec, lk1, obj); else thisObj->deleteProperty(exec, lk1); } return JSValue::encode(thisObj); } EncodedJSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState* exec) { JSObject* thisObj = exec->hostThisValue().toThisObject(exec); JSValue result; unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); if (length == 0) { putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length)); result = jsUndefined(); } else { result = thisObj->get(exec, 0); if (isJSArray(&exec->globalData(), thisObj)) ((JSArray *)thisObj)->shiftCount(exec, 1); else { for (unsigned k = 1; k < length; k++) { if (JSValue obj = getProperty(exec, thisObj, k)) thisObj->put(exec, k - 1, obj); else thisObj->deleteProperty(exec, k - 1); } thisObj->deleteProperty(exec, length - 1); } putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length - 1)); } return JSValue::encode(result); } EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState* exec) { // http://developer.netscape.com/docs/manuals/js/client/jsref/array.htm#1193713 or 15.4.4.10 JSObject* thisObj = exec->hostThisValue().toThisObject(exec); // We return a new array JSArray* resObj = constructEmptyArray(exec); JSValue result = resObj; unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length); unsigned end = argumentClampedIndexFromStartOrEnd(exec, 1, length, length); unsigned n = 0; for (unsigned k = begin; k < end; k++, n++) { if (JSValue v = getProperty(exec, thisObj, k)) resObj->put(exec, n, v); } resObj->setLength(n); return JSValue::encode(result); } EncodedJSValue JSC_HOST_CALL arrayProtoFuncSort(ExecState* exec) { JSObject* thisObj = exec->hostThisValue().toThisObject(exec); JSValue function = exec->argument(0); CallData callData; CallType callType = getCallData(function, callData); if (thisObj->classInfo() == &JSArray::info) { if (isNumericCompareFunction(exec, callType, callData)) asArray(thisObj)->sortNumeric(exec, function, callType, callData); else if (callType != CallTypeNone) asArray(thisObj)->sort(exec, function, callType, callData); else asArray(thisObj)->sort(exec); return JSValue::encode(thisObj); } unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); if (!length) return JSValue::encode(thisObj); // "Min" sort. Not the fastest, but definitely less code than heapsort // or quicksort, and much less swapping than bubblesort/insertionsort. for (unsigned i = 0; i < length - 1; ++i) { JSValue iObj = thisObj->get(exec, i); unsigned themin = i; JSValue minObj = iObj; for (unsigned j = i + 1; j < length; ++j) { JSValue jObj = thisObj->get(exec, j); double compareResult; if (jObj.isUndefined()) compareResult = 1; // don't check minObj because there's no need to differentiate == (0) from > (1) else if (minObj.isUndefined()) compareResult = -1; else if (callType != CallTypeNone) { MarkedArgumentBuffer l; l.append(jObj); l.append(minObj); compareResult = call(exec, function, callType, callData, exec->globalThisValue(), l).toNumber(exec); } else compareResult = (jObj.toString(exec) < minObj.toString(exec)) ? -1 : 1; if (compareResult < 0) { themin = j; minObj = jObj; } } // Swap themin and i if (themin > i) { thisObj->put(exec, i, minObj); thisObj->put(exec, themin, iObj); } } return JSValue::encode(thisObj); } EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState* exec) { JSObject* thisObj = exec->hostThisValue().toThisObject(exec); // 15.4.4.12 if (!exec->argumentCount()) return JSValue::encode(constructEmptyArray(exec)); unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length); unsigned deleteCount = length - begin; if (exec->argumentCount() > 1) { double deleteDouble = exec->argument(1).toInteger(exec); if (deleteDouble < 0) deleteCount = 0; else if (deleteDouble > length - begin) deleteCount = length - begin; else deleteCount = static_cast(deleteDouble); } JSArray* resObj = new (exec) JSArray(exec->lexicalGlobalObject()->arrayStructure(), deleteCount, CreateCompact); JSValue result = resObj; for (unsigned k = 0; k < deleteCount; k++) resObj->uncheckedSetIndex(k, getProperty(exec, thisObj, k + begin)); resObj->setLength(deleteCount); unsigned additionalArgs = std::max(exec->argumentCount() - 2, 0); if (additionalArgs != deleteCount) { if (additionalArgs < deleteCount) { if ((!begin) && (isJSArray(&exec->globalData(), thisObj))) ((JSArray *)thisObj)->shiftCount(exec, deleteCount - additionalArgs); else { for (unsigned k = begin; k < length - deleteCount; ++k) { if (JSValue v = getProperty(exec, thisObj, k + deleteCount)) thisObj->put(exec, k + additionalArgs, v); else thisObj->deleteProperty(exec, k + additionalArgs); } for (unsigned k = length; k > length - deleteCount + additionalArgs; --k) thisObj->deleteProperty(exec, k - 1); } } else { if ((!begin) && (isJSArray(&exec->globalData(), thisObj))) ((JSArray *)thisObj)->unshiftCount(exec, additionalArgs - deleteCount); else { for (unsigned k = length - deleteCount; k > begin; --k) { if (JSValue obj = getProperty(exec, thisObj, k + deleteCount - 1)) thisObj->put(exec, k + additionalArgs - 1, obj); else thisObj->deleteProperty(exec, k + additionalArgs - 1); } } } } for (unsigned k = 0; k < additionalArgs; ++k) thisObj->put(exec, k + begin, exec->argument(k + 2)); putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length - deleteCount + additionalArgs)); return JSValue::encode(result); } EncodedJSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState* exec) { JSObject* thisObj = exec->hostThisValue().toThisObject(exec); // 15.4.4.13 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); unsigned nrArgs = exec->argumentCount(); if ((nrArgs) && (length)) { if (isJSArray(&exec->globalData(), thisObj)) ((JSArray *)thisObj)->unshiftCount(exec, nrArgs); else { for (unsigned k = length; k > 0; --k) { if (JSValue v = getProperty(exec, thisObj, k - 1)) thisObj->put(exec, k + nrArgs - 1, v); else thisObj->deleteProperty(exec, k + nrArgs - 1); } } } for (unsigned k = 0; k < nrArgs; ++k) thisObj->put(exec, k, exec->argument(k)); JSValue result = jsNumber(length + nrArgs); putProperty(exec, thisObj, exec->propertyNames().length, result); return JSValue::encode(result); } EncodedJSValue JSC_HOST_CALL arrayProtoFuncFilter(ExecState* exec) { JSObject* thisObj = exec->hostThisValue().toThisObject(exec); JSValue function = exec->argument(0); CallData callData; CallType callType = getCallData(function, callData); if (callType == CallTypeNone) return throwVMTypeError(exec); JSObject* applyThis = exec->argument(1).isUndefinedOrNull() ? exec->globalThisValue() : exec->argument(1).toObject(exec); JSArray* resultArray = constructEmptyArray(exec); unsigned filterIndex = 0; unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); unsigned k = 0; if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) { JSFunction* f = asFunction(function); JSArray* array = asArray(thisObj); CachedCall cachedCall(exec, f, 3); for (; k < length && !exec->hadException(); ++k) { if (!array->canGetIndex(k)) break; JSValue v = array->getIndex(k); cachedCall.setThis(applyThis); cachedCall.setArgument(0, v); cachedCall.setArgument(1, jsNumber(k)); cachedCall.setArgument(2, thisObj); JSValue result = cachedCall.call(); if (result.toBoolean(exec)) resultArray->put(exec, filterIndex++, v); } if (k == length) return JSValue::encode(resultArray); } for (; k < length && !exec->hadException(); ++k) { PropertySlot slot(thisObj); if (!thisObj->getPropertySlot(exec, k, slot)) continue; JSValue v = slot.getValue(exec, k); MarkedArgumentBuffer eachArguments; eachArguments.append(v); eachArguments.append(jsNumber(k)); eachArguments.append(thisObj); JSValue result = call(exec, function, callType, callData, applyThis, eachArguments); if (result.toBoolean(exec)) resultArray->put(exec, filterIndex++, v); } return JSValue::encode(resultArray); } EncodedJSValue JSC_HOST_CALL arrayProtoFuncMap(ExecState* exec) { JSObject* thisObj = exec->hostThisValue().toThisObject(exec); JSValue function = exec->argument(0); CallData callData; CallType callType = getCallData(function, callData); if (callType == CallTypeNone) return throwVMTypeError(exec); JSObject* applyThis = exec->argument(1).isUndefinedOrNull() ? exec->globalThisValue() : exec->argument(1).toObject(exec); unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); JSArray* resultArray = constructEmptyArray(exec, length); unsigned k = 0; if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) { JSFunction* f = asFunction(function); JSArray* array = asArray(thisObj); CachedCall cachedCall(exec, f, 3); for (; k < length && !exec->hadException(); ++k) { if (UNLIKELY(!array->canGetIndex(k))) break; cachedCall.setThis(applyThis); cachedCall.setArgument(0, array->getIndex(k)); cachedCall.setArgument(1, jsNumber(k)); cachedCall.setArgument(2, thisObj); resultArray->JSArray::put(exec, k, cachedCall.call()); } } for (; k < length && !exec->hadException(); ++k) { PropertySlot slot(thisObj); if (!thisObj->getPropertySlot(exec, k, slot)) continue; JSValue v = slot.getValue(exec, k); MarkedArgumentBuffer eachArguments; eachArguments.append(v); eachArguments.append(jsNumber(k)); eachArguments.append(thisObj); JSValue result = call(exec, function, callType, callData, applyThis, eachArguments); resultArray->put(exec, k, result); } return JSValue::encode(resultArray); } // Documentation for these three is available at: // http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:every // http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:forEach // http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:some EncodedJSValue JSC_HOST_CALL arrayProtoFuncEvery(ExecState* exec) { JSObject* thisObj = exec->hostThisValue().toThisObject(exec); JSValue function = exec->argument(0); CallData callData; CallType callType = getCallData(function, callData); if (callType == CallTypeNone) return throwVMTypeError(exec); JSObject* applyThis = exec->argument(1).isUndefinedOrNull() ? exec->globalThisValue() : exec->argument(1).toObject(exec); JSValue result = jsBoolean(true); unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); unsigned k = 0; if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) { JSFunction* f = asFunction(function); JSArray* array = asArray(thisObj); CachedCall cachedCall(exec, f, 3); for (; k < length && !exec->hadException(); ++k) { if (UNLIKELY(!array->canGetIndex(k))) break; cachedCall.setThis(applyThis); cachedCall.setArgument(0, array->getIndex(k)); cachedCall.setArgument(1, jsNumber(k)); cachedCall.setArgument(2, thisObj); JSValue result = cachedCall.call(); if (!result.toBoolean(cachedCall.newCallFrame(exec))) return JSValue::encode(jsBoolean(false)); } } for (; k < length && !exec->hadException(); ++k) { PropertySlot slot(thisObj); if (!thisObj->getPropertySlot(exec, k, slot)) continue; MarkedArgumentBuffer eachArguments; eachArguments.append(slot.getValue(exec, k)); eachArguments.append(jsNumber(k)); eachArguments.append(thisObj); bool predicateResult = call(exec, function, callType, callData, applyThis, eachArguments).toBoolean(exec); if (!predicateResult) { result = jsBoolean(false); break; } } return JSValue::encode(result); } EncodedJSValue JSC_HOST_CALL arrayProtoFuncForEach(ExecState* exec) { JSObject* thisObj = exec->hostThisValue().toThisObject(exec); JSValue function = exec->argument(0); CallData callData; CallType callType = getCallData(function, callData); if (callType == CallTypeNone) return throwVMTypeError(exec); JSObject* applyThis = exec->argument(1).isUndefinedOrNull() ? exec->globalThisValue() : exec->argument(1).toObject(exec); unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); unsigned k = 0; if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) { JSFunction* f = asFunction(function); JSArray* array = asArray(thisObj); CachedCall cachedCall(exec, f, 3); for (; k < length && !exec->hadException(); ++k) { if (UNLIKELY(!array->canGetIndex(k))) break; cachedCall.setThis(applyThis); cachedCall.setArgument(0, array->getIndex(k)); cachedCall.setArgument(1, jsNumber(k)); cachedCall.setArgument(2, thisObj); cachedCall.call(); } } for (; k < length && !exec->hadException(); ++k) { PropertySlot slot(thisObj); if (!thisObj->getPropertySlot(exec, k, slot)) continue; MarkedArgumentBuffer eachArguments; eachArguments.append(slot.getValue(exec, k)); eachArguments.append(jsNumber(k)); eachArguments.append(thisObj); call(exec, function, callType, callData, applyThis, eachArguments); } return JSValue::encode(jsUndefined()); } EncodedJSValue JSC_HOST_CALL arrayProtoFuncSome(ExecState* exec) { JSObject* thisObj = exec->hostThisValue().toThisObject(exec); JSValue function = exec->argument(0); CallData callData; CallType callType = getCallData(function, callData); if (callType == CallTypeNone) return throwVMTypeError(exec); JSObject* applyThis = exec->argument(1).isUndefinedOrNull() ? exec->globalThisValue() : exec->argument(1).toObject(exec); JSValue result = jsBoolean(false); unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); unsigned k = 0; if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) { JSFunction* f = asFunction(function); JSArray* array = asArray(thisObj); CachedCall cachedCall(exec, f, 3); for (; k < length && !exec->hadException(); ++k) { if (UNLIKELY(!array->canGetIndex(k))) break; cachedCall.setThis(applyThis); cachedCall.setArgument(0, array->getIndex(k)); cachedCall.setArgument(1, jsNumber(k)); cachedCall.setArgument(2, thisObj); JSValue result = cachedCall.call(); if (result.toBoolean(cachedCall.newCallFrame(exec))) return JSValue::encode(jsBoolean(true)); } } for (; k < length && !exec->hadException(); ++k) { PropertySlot slot(thisObj); if (!thisObj->getPropertySlot(exec, k, slot)) continue; MarkedArgumentBuffer eachArguments; eachArguments.append(slot.getValue(exec, k)); eachArguments.append(jsNumber(k)); eachArguments.append(thisObj); bool predicateResult = call(exec, function, callType, callData, applyThis, eachArguments).toBoolean(exec); if (predicateResult) { result = jsBoolean(true); break; } } return JSValue::encode(result); } EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduce(ExecState* exec) { JSObject* thisObj = exec->hostThisValue().toThisObject(exec); JSValue function = exec->argument(0); CallData callData; CallType callType = getCallData(function, callData); if (callType == CallTypeNone) return throwVMTypeError(exec); unsigned i = 0; JSValue rv; unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); if (!length && exec->argumentCount() == 1) return throwVMTypeError(exec); JSArray* array = 0; if (isJSArray(&exec->globalData(), thisObj)) array = asArray(thisObj); if (exec->argumentCount() >= 2) rv = exec->argument(1); else if (array && array->canGetIndex(0)){ rv = array->getIndex(0); i = 1; } else { for (i = 0; i < length; i++) { rv = getProperty(exec, thisObj, i); if (rv) break; } if (!rv) return throwVMTypeError(exec); i++; } if (callType == CallTypeJS && array) { CachedCall cachedCall(exec, asFunction(function), 4); for (; i < length && !exec->hadException(); ++i) { cachedCall.setThis(jsNull()); cachedCall.setArgument(0, rv); JSValue v; if (LIKELY(array->canGetIndex(i))) v = array->getIndex(i); else break; // length has been made unsafe while we enumerate fallback to slow path cachedCall.setArgument(1, v); cachedCall.setArgument(2, jsNumber(i)); cachedCall.setArgument(3, array); rv = cachedCall.call(); } if (i == length) // only return if we reached the end of the array return JSValue::encode(rv); } for (; i < length && !exec->hadException(); ++i) { JSValue prop = getProperty(exec, thisObj, i); if (!prop) continue; MarkedArgumentBuffer eachArguments; eachArguments.append(rv); eachArguments.append(prop); eachArguments.append(jsNumber(i)); eachArguments.append(thisObj); rv = call(exec, function, callType, callData, jsNull(), eachArguments); } return JSValue::encode(rv); } EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduceRight(ExecState* exec) { JSObject* thisObj = exec->hostThisValue().toThisObject(exec); JSValue function = exec->argument(0); CallData callData; CallType callType = getCallData(function, callData); if (callType == CallTypeNone) return throwVMTypeError(exec); unsigned i = 0; JSValue rv; unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); if (!length && exec->argumentCount() == 1) return throwVMTypeError(exec); JSArray* array = 0; if (isJSArray(&exec->globalData(), thisObj)) array = asArray(thisObj); if (exec->argumentCount() >= 2) rv = exec->argument(1); else if (array && array->canGetIndex(length - 1)){ rv = array->getIndex(length - 1); i = 1; } else { for (i = 0; i < length; i++) { rv = getProperty(exec, thisObj, length - i - 1); if (rv) break; } if (!rv) return throwVMTypeError(exec); i++; } if (callType == CallTypeJS && array) { CachedCall cachedCall(exec, asFunction(function), 4); for (; i < length && !exec->hadException(); ++i) { unsigned idx = length - i - 1; cachedCall.setThis(jsNull()); cachedCall.setArgument(0, rv); if (UNLIKELY(!array->canGetIndex(idx))) break; // length has been made unsafe while we enumerate fallback to slow path cachedCall.setArgument(1, array->getIndex(idx)); cachedCall.setArgument(2, jsNumber(idx)); cachedCall.setArgument(3, array); rv = cachedCall.call(); } if (i == length) // only return if we reached the end of the array return JSValue::encode(rv); } for (; i < length && !exec->hadException(); ++i) { unsigned idx = length - i - 1; JSValue prop = getProperty(exec, thisObj, idx); if (!prop) continue; MarkedArgumentBuffer eachArguments; eachArguments.append(rv); eachArguments.append(prop); eachArguments.append(jsNumber(idx)); eachArguments.append(thisObj); rv = call(exec, function, callType, callData, jsNull(), eachArguments); } return JSValue::encode(rv); } EncodedJSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState* exec) { // JavaScript 1.5 Extension by Mozilla // Documentation: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:indexOf JSObject* thisObj = exec->hostThisValue().toThisObject(exec); unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); unsigned index = argumentClampedIndexFromStartOrEnd(exec, 1, length); JSValue searchElement = exec->argument(0); for (; index < length; ++index) { JSValue e = getProperty(exec, thisObj, index); if (!e) continue; if (JSValue::strictEqual(exec, searchElement, e)) return JSValue::encode(jsNumber(index)); } return JSValue::encode(jsNumber(-1)); } EncodedJSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState* exec) { // JavaScript 1.6 Extension by Mozilla // Documentation: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:lastIndexOf JSObject* thisObj = exec->hostThisValue().toThisObject(exec); unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); if (!length) return JSValue::encode(jsNumber(-1)); unsigned index = length - 1; JSValue fromValue = exec->argument(1); if (!fromValue.isUndefined()) { double fromDouble = fromValue.toInteger(exec); if (fromDouble < 0) { fromDouble += length; if (fromDouble < 0) return JSValue::encode(jsNumber(-1)); } if (fromDouble < length) index = static_cast(fromDouble); } JSValue searchElement = exec->argument(0); do { ASSERT(index < length); JSValue e = getProperty(exec, thisObj, index); if (!e) continue; if (JSValue::strictEqual(exec, searchElement, e)) return JSValue::encode(jsNumber(index)); } while (index--); return JSValue::encode(jsNumber(-1)); } } // namespace JSC
生成的ArrayPrototype.lut.h内容如下(D:/tools/cygwin/home/xufan/WebKit/WebKitBuild/obj/JavaScriptCore/DerivedSources/ArrayPrototype.lut.h):
// Automatically generated from /cygdrive/d/tools/cygwin/home/xufan/WebKit/JAVASC~1/runtime/ArrayPrototype.cpp using /cygdrive/d/tools/cygwin/home/xufan/WebKit/JAVASC~1/create_hash_table. DO NOT EDIT! #include "Lookup.h" namespace JSC { #if ENABLE(JIT) #define THUNK_GENERATOR(generator) , generator #else #define THUNK_GENERATOR(generator) #endif static const struct HashTableValue arrayTableValues[22] = { { "toString", DontEnum|Function, (intptr_t)static_cast(arrayProtoFuncToString), (intptr_t)0 THUNK_GENERATOR(0) }, { "toLocaleString", DontEnum|Function, (intptr_t)static_cast(arrayProtoFuncToLocaleString), (intptr_t)0 THUNK_GENERATOR(0) }, { "concat", DontEnum|Function, (intptr_t)static_cast(arrayProtoFuncConcat), (intptr_t)1 THUNK_GENERATOR(0) }, { "join", DontEnum|Function, (intptr_t)static_cast(arrayProtoFuncJoin), (intptr_t)1 THUNK_GENERATOR(0) }, { "pop", DontEnum|Function, (intptr_t)static_cast(arrayProtoFuncPop), (intptr_t)0 THUNK_GENERATOR(0) }, { "push", DontEnum|Function, (intptr_t)static_cast(arrayProtoFuncPush), (intptr_t)1 THUNK_GENERATOR(0) }, { "reverse", DontEnum|Function, (intptr_t)static_cast(arrayProtoFuncReverse), (intptr_t)0 THUNK_GENERATOR(0) }, { "shift", DontEnum|Function, (intptr_t)static_cast(arrayProtoFuncShift), (intptr_t)0 THUNK_GENERATOR(0) }, { "slice", DontEnum|Function, (intptr_t)static_cast(arrayProtoFuncSlice), (intptr_t)2 THUNK_GENERATOR(0) }, { "sort", DontEnum|Function, (intptr_t)static_cast(arrayProtoFuncSort), (intptr_t)1 THUNK_GENERATOR(0) }, { "splice", DontEnum|Function, (intptr_t)static_cast(arrayProtoFuncSplice), (intptr_t)2 THUNK_GENERATOR(0) }, { "unshift", DontEnum|Function, (intptr_t)static_cast(arrayProtoFuncUnShift), (intptr_t)1 THUNK_GENERATOR(0) }, { "every", DontEnum|Function, (intptr_t)static_cast(arrayProtoFuncEvery), (intptr_t)1 THUNK_GENERATOR(0) }, { "forEach", DontEnum|Function, (intptr_t)static_cast(arrayProtoFuncForEach), (intptr_t)1 THUNK_GENERATOR(0) }, { "some", DontEnum|Function, (intptr_t)static_cast(arrayProtoFuncSome), (intptr_t)1 THUNK_GENERATOR(0) }, { "indexOf", DontEnum|Function, (intptr_t)static_cast(arrayProtoFuncIndexOf), (intptr_t)1 THUNK_GENERATOR(0) }, { "lastIndexOf", DontEnum|Function, (intptr_t)static_cast(arrayProtoFuncLastIndexOf), (intptr_t)1 THUNK_GENERATOR(0) }, { "filter", DontEnum|Function, (intptr_t)static_cast(arrayProtoFuncFilter), (intptr_t)1 THUNK_GENERATOR(0) }, { "reduce", DontEnum|Function, (intptr_t)static_cast(arrayProtoFuncReduce), (intptr_t)1 THUNK_GENERATOR(0) }, { "reduceRight", DontEnum|Function, (intptr_t)static_cast(arrayProtoFuncReduceRight), (intptr_t)1 THUNK_GENERATOR(0) }, { "map", DontEnum|Function, (intptr_t)static_cast(arrayProtoFuncMap), (intptr_t)1 THUNK_GENERATOR(0) }, { 0, 0, 0, 0 THUNK_GENERATOR(0) } }; #undef THUNK_GENERATOR extern JSC_CONST_HASHTABLE HashTable arrayTable = { 65, 63, arrayTableValues, 0 }; } // namespace
ArrayPrototype.lut.h基本将js里面的Array对象的成员方法都列举出来了。
Lexer.lut.h由D:/tools/cygwin/home/xufan/WebKit/JavaScriptCore/parser/Keywords.table这个文件生成,这个文件里面包括了js语法里面的关键字。
chartables.c由D:/tools/cygwin/home/xufan/WebKit/JavaScriptCore/pcre/dftables这个perl脚本生成,主要包括小于128的ASCII字符。
bytecode.html(D:/tools/cygwin/home/xufan/WebKit/WebKitBuild/obj/JavaScriptCore/DerivedSources/docs/bytecode.html)由Interpreter.cpp通过make-bytecode-docs.pl工具生成,里面有对js bytecode的解释,这对于理解js的jit机制是很有帮助的,大致格式如下:
new_object
Format: new_object dst(r)
Constructs a new empty Object instance using the original constructor, and puts the result in register dst.
new_array
Format: new_array dst(r) firstArg(r) argCount(n)
Constructs a new Array instance using the original constructor, and puts the result in register dst. The array will contain argCount elements with values taken from registers starting at register firstArg.
顺便插一句,dalvik的bytecode文档可以参考:http://www.netmite.com/android/mydroid/dalvik/docs/dalvik-bytecode.html。
RegExpJitTables.h由create_regex_tables这个python脚本生成,主要用于yarr(Yet Another Regex Runtime)的jit生成。http://hanblog.info/blog/post/2009/04/23/WebKit-s-week-8里面有对yarr的简单介绍,yarr的svn log:http://celeron.1997open.com/browse.php?u=%3D%3DQM4QjM08CdlNXZn5WYoN2LnJ3buQXarJWZ35yYhJHdv8iO&b=1。
JavaScriptCore.JSVALUE32.exp等exp文件主要用于生成dll时的导出函数,生成规则很简单,将多个exp文件用cat合并到一个文件。
回到JavaScriptCoreGenerated.make这个文件,在执行完build-generated-files.sh,就调用react-to-vsprops-changes.py这个脚本对vsprops做一些处理,主要是处理manifest文件和Platform.h文件。之后就开始拷贝文件和建目录。补充一个小常识,makefile action中-(减号)代表即时这条命令出错,也继续走下去。如果对makefile不熟悉,强烈推荐看陈皓写的《跟我一起写makefile》。
至此,JavaScriptCoreGenerated工程的编译规则写完了,明天接着写WTF工程和JavaScriptCore工程。